【问题标题】:Makefile Include directive while migrating from Unix to Linux从 Unix 迁移到 Linux 时的 Makefile Include 指令
【发布时间】:2017-01-10 16:04:19
【问题描述】:

我的任务是将古老的基于 C++ 的代码从 Unix 环境迁移到 Linux。 该项目由多个 Makefile 组成,用于库的不同“模块”。 我已经解决了一些问题,但是现在遇到了包含指令的问题。

显然,Makefile 的构造方式是为不同的文件设置一个单独的包含指令,并且它在 Unix 服务器中工作了多年。

例如:

include ../../../../util/genmake.def

processControl.slOBJS = processControl.o
outputControlOBJS = outputControl.o
inputControlOBJS = inputControl.o
cleanList  = *.o *.C *.out processControl.sl outputControl inputControl

all: processControl.sl  outputControl inputControl

processControl.sl: $(processControl.slOBJS)
        include ../../../../util/genmake.slinc

outputControl: $(outputControlOBJS)
        include ../../../../util/genmake.exeinc

inputControl: $(inputControlOBJS)
        include ../../../../util/genmake.exeinc

include ../../../../util/genmake.inc

你可以在这里看到,标签式包含,只指指定的目标!它们是实现该目标的秘诀的一部分。

但是,这个结构在 Linux 中不起作用,我得到这个错误:

include ../../../../util/genmake.slinc
make: include: Command not found
make: *** [processControl.sl] Error 127

显然我不能只删除选项卡,因为包含应该只针对该目标...

所以,我尝试用 shell 'source' 命令替换它来获取包含的文件,例如:

        source ../../../../util/genmake.slinc

这显然不起作用,我还尝试将包含的代码直接引入到包含文件中(注释掉包含命令) - 这会处理与 Makefile 相关的错误,但这不是一个好的解决方案,因为它会迁移和维护它真的很困难,必须将整个项目中的相同代码复制到所有模块中,然后当然每一个微小的变化我都必须反映在所有文件中。

任何Make专家都可以就这个问题提出建议吗? 此外,通常欢迎任何有关如何最好地处理此迁移任务的建议:)

谢谢!

编辑- 额外信息: genmake.slinc 的内容:


#######################################################################
## This include will inherit the target and dependent files in
## a make file that inlcudes it.
## The inclusion of this file should look like:
##
## {target}: {dependent file list}
## include genmake.slinc
##
#######################################################################
        @echo "----------------------------------------------------------------"
        @echo " (G) Creating shared library $@ from $($(MaKeObJs:x=$(@)OBJS))";
        @echo "----------------------------------------------------------------"
        @echo "$(CPP) -b -o $@ $($(MaKeObJs:x=$(@)OBJS))"
        @$(CPP) -b -o $@ $($(MaKeObJs:x=$(@)OBJS))
        @echo " Moving shared library to $(SHLIBINSTALL) as lib$(@)"
        @$(MV) $@ $(SHLIBINSTALL)/lib$(@)
        @echo "----------------------------------------------------------------"

那些是@之前的制表符。

现在genmake.exeinc的内容:


#######################################################################
## This include will inherit the target and dependent files in
## a make file that inlcudes it.
## The inclusion of this file should look like:
##
## {target}: {dependent file list}
## include /opt/app/fba/util/genmake.exeinc
##
#######################################################################
        @cp $(SHLIBINSTALL)/* $(TEMP_SHLIB_DIR)/.
        @echo "----------------------------------------------------------------"
        @echo " (G) Linking $($(MaKeObJs:x=$(@)OBJS)) to make $@ "
        @echo "     LDOPTS set to: $(LDOPTS)"
#       @echo "  $(PURIFY) $(LD) "
        @echo "  SHLIB Temp Path: $(TEMP_SHLIB_DIR)
        @echo "     FBA libraries: $(FBALIB)"
        @echo "     Application libraries: $(APPLIBS)"
        $(PURIFY) $(LD) -o $@ $($(MaKeObJs:x=$(@)OBJS)) \
                $(CXXOPTS) \
                $(LDFLAGS) \
                $(FBALIB) \
                $(PROLDLIBS) \
                `cat /usr/local/opt/oracle/product/t1c3d771/lib/ldflags`  \
                `cat /usr/local/opt/oracle/product/t1c3d771/lib/sysliblist` \
                $(APPLIBS)
        @echo " Moving executable to $(EXEINSTALL) "
        @$(MV) $@ $(EXEINSTALL)
        @echo "----------------------------------------------------------------"

如果我转到 Makefile,并删除包含的前导选项卡,我会收到以下错误: ../../../../util/genmake.slinc:10: *** 命令在第一个目标之前开始。停下来。

【问题讨论】:

  • @DOUG 如果删除标签,则包含不再是配方的一部分。
  • 来自 GNU Make:“行首允许并忽略多余的空格,但第一个字符不能是制表符(或 .RECIPEPREFIX 的值)——如果行以选项卡,它将被视为一个配方行。包含和文件名之间以及文件名之间需要空格;额外的空格在该处和指令末尾被忽略。最后允许以“#”开头的注释行的。如果文件名包含任何变量或函数引用,它们将被扩展。请参阅如何使用变量。“也许 Make 已更改?
  • 如果 make 确实从 UNIX 更改为 Linux,您建议我如何解决 make 文件中每个目标的多个不同包含的问题?也很高兴确切地知道此行为在哪个版本中发生了更改,因为这听起来像是从更高级的功能退了一步。
  • 有趣。我(目前)认为 Make 在开始处理任何规则之前会读取所有 makefile 和所有包含 makefile。因此,包含顺序(w.r.t. 配方行)不应该有所作为。恕我直言,你的例子似乎与此相矛盾。您是否尝试过删除选项卡以查看构建是否仍然有效?如果它们相互矛盾,不知道该怎么办。
  • @Doug:我不在办公室,明天会检查,尽管我认为我确实尝试过.. 不记得结果是什么,也不记得为什么它不可接受。明天早上更新。

标签: c++ makefile gnu-make


【解决方案1】:

我让 gnumake 完成所有处理的想法被证明是错误的。 HP-UX 11i Version 1 与最新的 Linux 版本的相关差异如下:

  • TABinclude 之前被默默忽略;现在它被解释为配方前缀,并且 gnu make 尝试在您的机器上查找 include 可执行文件。

  • include in gnu make 在合并的输出中悄悄地添加一个空行在每个包含的边界上重置配方> 文件(正如MadScientist 正确指出的那样),因此包含的文件不能仅包含配方语句。

如果包含的文件不递归使用相同的技巧(在您的示例中,它们似乎是简单的操作,没有额外的 include 语句),您可以使用 awk 像这样动态准备合并的 Makefile:

awk '{ if (NF == 2 && $1 == "include") { while ((getline line < $2) > 0) print line ; close($2) } else print }' Makefile | make -f -

上面的脚本将处理您系统上的文件,无需编辑。

【讨论】:

  • 我想我现在明白了,它在命令行运行。它给出以下错误: make: /dev/stdin: No such device or address make: *** No targets。停止。
  • 我通过手动定向到临时 make 文件输出然后使用 make -f 进行测试 - 它有效! (落在其他错误上,但这不是重点) - 谢谢!只需要解决标准输入错误...
  • 另外,我想了解一下您是如何调试得出这个结论的:“include in gnu make 会默默地在合并的输出中添加一个空行”
  • “静默加空行”的说法是不正确的。有可能作者认为,如果在规则介绍和菜谱之间有一个空行,那么菜谱不是规则的一部分,但这是不正确的:你可以在菜谱中穿插空白行、cmets、ifdefs 等。它比这更简单:在 GNU make 中,当解析 include 语句时,在包含文件之前,任何正在进行的规则定义都已完成。规则定义不能跨越 GNU make 中的包含文件。
  • @MadScientist 你是对的!这确实比我预期的更棘手的逻辑。感谢指正!
【解决方案2】:

只需将目标作为变量添加到包含中: 例如,genmake.exeinc 将变为:

$(targetControl): $(depControlOBJS)
    @cp $(SHLIBINSTALL)/* $(TEMP_SHLIB_DIR)/.
    @echo "----------------------------------------------------------------"
    @echo " (G) Linking $($(MaKeObJs:x=$(@)OBJS)) to make $@ "
    @echo "     LDOPTS set to: $(LDOPTS)"
#   @echo "  $(PURIFY) $(LD) "
    @echo "  SHLIB Temp Path: $(TEMP_SHLIB_DIR)
    @echo "     FBA libraries: $(FBALIB)"
    @echo "     Application libraries: $(APPLIBS)"
    $(PURIFY) $(LD) -o $@ $($(MaKeObJs:x=$(@)OBJS)) \
            $(CXXOPTS) \
            $(LDFLAGS) \
            $(FBALIB) \
            $(PROLDLIBS) \
            `cat /usr/local/opt/oracle/product/t1c3d771/lib/ldflags`  \
            `cat /usr/local/opt/oracle/product/t1c3d771/lib/sysliblist` \
            $(APPLIBS)
    @echo " Moving executable to $(EXEINSTALL) "
    @$(MV) $@ $(EXEINSTALL)
    @echo "----------------------------------------------------------------"

然后修改你的makefile的相关部分如下:

#outputControl: $(outputControlOBJS)
targetControl=outputControl
depControlOBJS=$(outputControlOBJS)
include ../../../../util/genmake.exeinc

#inputControl: $(inputControlOBJS)
targetControl=inputControl
depControlOBJS=$(inputControlOBJS)
include ../../../../util/genmake.exeinc

【讨论】:

  • 漂亮的解决方案!谢谢你!虽然其他 awk 解决方案很聪明,但毕竟它并不优雅......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-26
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
  • 2016-08-02
  • 2018-02-11
  • 1970-01-01
相关资源
最近更新 更多