【问题标题】:How do make dependency generation work for C? (Also..decode this sed/make statement!)如何使依赖生成对 C 起作用? (也..解码这个 sed/make 语句!)
【发布时间】:2010-05-26 18:46:04
【问题描述】:

我有一个 make 构建系统,我正试图破译其他人编写的内容。当我在 redhat 系统上运行它时出现错误,但在我的 solaris 系统上运行它时却没有。 gmake 的版本是相同的主要版本(次要版本关闭)。

这是为了构建一个C项目,make系统有一个全局的Makefile.global,被每个目录的本地Makefile继承

Makefile.global 包含所有目标,以

开头
 all: $(LIB) $(BIN)

全局文件也有下面这行

 ifndef INCLUDE_GEN_DEPS
 sinclude $(GEN_DEPS)
 endif

LIB 构建库,BIN 构建二进制文件。

跳下我的目标

 $(LIB) : $(GEN_LIB) 

 $(GEN_LIB) : $(GEN_DEPS) $(GEN_OBJS)
      $(AR) $(ARFLAGS) $(GEN_LIB) $(GEN_OBJS)
 $(GEN_DEPS) :
      @set -e; rm -f $@; \
      $(CC) $(CDEP_FLAG) $(CFLAGS) $(INCDIRS) `basename $@ | sed 's/\.d/\.c/' | sed 's,^,$(HOME_SRC)/,'` | sed 's,\(.*\)\.o: ,$(GEN_OBJDIR)/\1.o $@ :,g' > $@.tmp ; \
      cat $@.tmp > $@ ; \
      cat $@.tmp | cut -d: -f2 | grep '\.h' | sed 's,\.h,.h :,g' >> $@ ; \
      rm $@.tmp
  $(GEN_OBJS) :
       $(CC) $(CFLAGS) $(INCDIRS) -c $(*F).c -lmpi -o $@

我认为这些是回答我的问题需要包括的所有相关目标。

这些变量的定义:

 CC = icc
 CDEP_FLAG = -M
 CFLAGS = various compiler flags ifdef type flags
 INCDIRS = include directory where all .h files are
 GEN_OBJDIR = /lib/objs
 HOME_SRC = .
 GEN_LIB = lib/$(LIB)
 GEN_DEPDIR=/lib/deps
 GEN_DEPS = $(addprefix $(GEN_DEPDIR)/,$(addsuffix .d,$(basename $(OBJS))))

我认为这涵盖了您需要的一切。基本上从名称中可以自我解释。

现在据我所知,这是在 /lib/deps 中生成一个 .d 文件,其中包含对象和源依赖项。换句话说,对于utilities.a库,我会得到一个utils.o和utils.c的依赖栈,都在utils.d文件中

我认为该文件中正在生成一些语法错误,因为我收到以下错误:

 ../lib/deps/util.d:25: *** target pattern contains no '%'. Stop.
 gmake[2]: *** [all] Error 2
 gmake[1]: *** [all] Error 2
 gmake: *** [all] Error 2

我不确定我的错误是在依赖生成中,还是在更底层的部分,比如对象生成目标?

如果您需要更多信息,请告诉我,我会添加到帖子中

【问题讨论】:

  • 您是否尝试过使用 -d 选项运行 gmake 以查看它在做什么?

标签: sed dependencies makefile redhat gnu-make


【解决方案1】:

比较在您的工作(Solaris)机器上写入 lib/deps/util.d 的内容与非工作(redhat)机器上写入的内容,特别注意第 25 行。这可能会给您一个很好的提示,了解正在发生的事情错了。

如果它们相同,则问题在于一次性制作次要版本。

如果你不同,问题可能是正在运行的工具之间存在一些差异,或者(更有可能)安装的 icc 版本不同。

编辑

解码规则:

$(GEN_DEPS) :
        @set -e; rm -f $@; \
        $(CC) $(CDEP_FLAG) $(CFLAGS) $(INCDIRS) `basename $@ | sed 's/\.d/\.c/' | sed 's,^,$(HOME_SRC)/,'` | sed 's,\(.*\)\.o: ,$(GEN_OBJDIR)/\1.o $@ :,g' > $@.tmp ; \
        cat $@.tmp > $@ ; \
        cat $@.tmp | cut -d: -f2 | grep '\.h' | sed 's,\.h,.h :,g' >> $@ ; \
        rm $@.tmp

这条规则使得$(GEN_DEPS)中的依赖文件,对应所有.c的源文件都改为.d

@set -e; rm -f $@;

导致任何错误立即退出此规则并失败,而不是继续,并删除我们将要重新生成的目标

$(CC) $(CDEP_FLAG) $(CFLAGS) $(INCDIRS) `basename $@ | sed 's/\.d/\.c/' | sed 's,^,$(HOME_SRC)/,'` | sed 's,\(.*\)\.o: ,$(GEN_OBJDIR)/\1.o $@ :,g' > $@.tmp ;

在与我们尝试生成的 .d 文件相对应的 .c 文件上运行 icc 并使用标志自动生成依赖项而不是编译。 basename $@ | sed 's/\.d/\.c/' | sed 's,^,$(HOME_SRC)/,' 子句将 .d 名称翻译回 .c 名称。

将输出(即依赖规则)传送到 sed 脚本 sed 's,\(.*\)\.o: ,$(GEN_OBJDIR)/\1.o $@ :,g',这会将依赖文件本身添加为依赖于 icc 为 .o(对象)文件找到的所有内容所依赖的东西.将所有这些写入一个临时文件。

cat $@.tmp > $@ ;

将临时文件复制到输出依赖文件。

cat $@.tmp | cut -d: -f2 | grep '\.h' | sed 's,\.h,.h :,g' >> $@ ;

将依赖项的第二个副本附加到依赖项文件中,由一个小脚本修改,该脚本剥离目标并将第一个标头放入目标中。所以这是为源文件中出现的第一个头文件添加一组额外的依赖项。

rm $@.tmp

删除临时文件。

【讨论】:

  • 我会检查的。我没有考虑编译器生成不同依赖项的可能性。我实际上是在 solaris 系统上使用 cc(即 SGI MIPS)
  • 这几乎肯定是问题所在——不同的编译器倾向于使用不同的标志来生成依赖项并以稍微不同的方式生成它们,这将导致 makefile 中的 sed 命令出现问题。
  • 太棒了。我会去检查差异,看看发生了什么。您认为解决此问题的最佳方法是什么?每个平台都有一套不同的制作规则吗?我认为从现在开始我们将主要坚持使用英特尔架构(使用 icc),但总有可能将其移植到其他地方。
  • 如果 icc 产生的结果足够接近,您可以修改 sed 命令以使其适用于任一编译器。否则,您最好将此操作移到单独的脚本中,并为不同平台设置多个版本的脚本。
猜你喜欢
  • 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
相关资源
最近更新 更多