错误 `*** 缺少分隔符。 Stop.`:Ubuntu 和 MacO 上的 GNU Make 序言中的 if 语句是否存在细微差别?

Error `*** missing separator. Stop.`: Is there a subtle difference in if statements in GNU Make preambles on Ubuntu and MacOs?

当我运行 makefile

 SHELL=/bin/bash
 
 a = False
 ifeq ("", "")
   a = True
 endif
 
 #b = False
 #ifeq("","")
 #  b = True
 #endif
 
 #c = False
 #if [ "" == "" ] \
 #then \
 #  c = True \
 #fi
 
 #d = False
 #if [[ "" == "" ]] \
 #then \
 #  d = True \
 #fi

 all :
     @\
     echo a = $(a); \
     echo b = $(b); \
     echo c = $(c); \
     echo d = $(d); \
     \
     c_loc=False; \
     if [ "" == "" ]; \
     then \
       c_loc=True; \
     fi; \
     \
     d_loc=False; \
     if [[ "" == "" ]]; \
     then \
       d_loc=True; \
     fi; \
     \
     echo c_loc = $${c_loc}; \
     echo d_loc = $${d_loc}; \

我得到了输出

a = True
b =
c =
d =
c_loc = True
d_loc = True

在 Ubuntu 20.04 上使用 GNU Make 4.2.1 时取消注释 if 语句 c 或 d 会导致错误 Makefile:n: *** missing separator. Stop.n是if语句c或d的行号。

另一方面,如果我 运行 在 Mac OS Big Sur v11.6.1 和 GNU Make3.81 上使用相同的 makefile(带有 if 语句 c 和 d 未注释)我得到输出

a = True
b =
c = False
d = False
c_loc = True
d_loc = True

取消注释 if 语句 b 导致两个设置中缺少分隔符错误。

我的问题:

我不知道你所说的“序言”是什么意思;生成文件中没有“序言”之类的东西。 Makefile 不是 shell 脚本,尽管它们在食谱中包含 shell 脚本。唯一可以使用 shell 脚本语法的地方是食谱。自 1970 年代发明 make 以来,情况一直如此。 if [...]; then; fi 等内容是 shell 脚本语法,因此它只能出现在食谱中。

如果你想在食谱之外使用 if 语句,你必须使用 makefile syntax,而不是 shell 语法。

至于为什么它在 GNU make 3.81 中“有效”(注意,它 真的 无效,因为它不会重置 c变量,不管你在条件中输入什么值),这是因为在旧版本的 GNU 中,过去创建由空格组成的变量名是合法的。所以这个:

c = False
if [ "" == "" ] \
then \
    c = True \
fi

被解释为这两行(反斜杠换行减去后):

c = False
if [ "" == "" ] then c = True fi

在旧版本的 GNU make 中,变量名可以包含空格,这会将名为 if [ "" == "" ] then c 的变量设置为值 True fi。这是合法的语法,但它显然没有达到您的预期。

在较新版本的 GNU make 中,我们不允许变量名包含空格,因此上面的文本是无效语法,您会得到 make 的标准语法错误缺少分隔符.