C宏和makefile和表达式无法按预期工作

时间:2017-03-29 11:00:10

标签: c bash makefile linux-kernel

在我的C代码中,我需要检查我的内核版本并根据它行事。 在makefile中,我有以下内容:

KERNEL_MAJOR :=$(word 1, $(subst ., ,$(KERNEL_HEADERS)))
KERNEL_MINOR :=$(word 2, $(subst ., ,$(KERNEL_HEADERS)))
KERNEL_MICRO :=$(word 1, $(subst -, ,$(word 3, $(subst ., ,$(KERNEL_HEADERS)))))
KERNEL_PATCH_LEVEL :=$(word 1, $(subst ., ,$(word 2, $(subst -, ,$(KERNEL_HEADERS)))))
KARGS := KCPPFLAGS="-DKERNEL_MAJOR=$(KERNEL_MAJOR) -DKERNEL_MINOR=$(KERNEL_MINOR) -DKERNEL_MICRO=$(KERNEL_MICRO) -DKERNEL_PATCH_LEVEL=$(KERNEL_PATCH_LEVEL)"

样品:
3.0.101-0.47.71-default 如下所示:

KCPPFLAGS="-DKERNEL_MAJOR=3 -DKERNEL_MINOR=0 -DKERNEL_MICRO=101 -DKERNEL_PATCH_LEVEL=0"

4.1.21.x86_64.1 (请注意KERNEL_PATCH_LEVEL):

KCPPFLAGS="-DKERNEL_MAJOR=4 -DKERNEL_MINOR=1 -DKERNEL_MICRO=21 -DKERNEL_PATCH_LEVEL="

我的代码中有宏来检查内核是否为3.0.101.0或3.0.76.0:

#if defined(KERNEL_MAJOR) && defined(KERNEL_MINOR) && defined(KERNEL_MICRO) && defined(KERNEL_PATCH_LEVEL) && \
    (KERNEL_VERSION(KERNEL_MAJOR,KERNEL_MINOR,KERNEL_MICRO) == KERNEL_VERSION(3,0,101) || KERNEL_VERSION(KERNEL_MAJOR,KERNEL_MINOR,KERNEL_MICRO) == KERNEL_VERSION(3,0,76)) \
    && (KERNEL_PATCH_LEVEL == 0)

有3&&布尔表达式,如果表达式#1没问题,那么我想继续表达#2,如果表达式#2继续,我将继续表达#3。 当我尝试在内核版本4.1.21.x86_64.1上编译(make ..)时,我收到:

error: operator '==' has no left operand

这是因为 -DKERNEL_PATCH_LEVEL =“ - 请参阅输出。
我希望制作可以阻止我进入那个&&因为表达式#2失败(3.0.101或3.0.76)

3 个答案:

答案 0 :(得分:1)

如果您阅读:https://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp_4.html,您会发现:

  

`#if'指令允许您测试算术表达式的值,而不仅仅是存在一个宏。它的语法是

     

#if 表达 ...

     

表达式是整数类型的C表达式,受到严格的限制。它可能包含:

     

...

     
      
  • 宏。 表达式中的所有宏在表达式值的实际计算开始之前都会展开

  •   
  • 不是宏的标识符,它们都被认为是数字零。 ...

  •   

所以如果你有:

#if defined(FOO) && FOO == 1

然后没有定义foo,它会将表达式解析为:

0 && 0 == 1

然后将解析为#if 0,这是有效的。但是,如果您将FOO定义为空白,则它将解析为:

1 && == 1

然后预编译器将尝试解析表达式,并获得语法错误,并失败。尽管网上似乎有一些评论,但预编译器并没有将表达式的解析短路,只是表达式的评估。

如果您想解决这个问题,可以使用以下宏观技巧:

#define COMBINE1(W,Y,Z) W##Y##Z
#define COMBINE(W,Y,Z) COMBINE1(W,Y,Z)
#define ISEMPTY(val)  COMBINE(val,4,val) == 4

#if defined FOO
#if ISEMPTY(FOO)
#pragma message "FOO DEFINED AS EMPTY"
#else
#pragma message "FOO DEFINED (NOT EMPTY)"
#endif
#else
#pragma message "FOO NOT DEFINED"
#endif

给出:

~/sandbox/tst6> gcc tst2.c
tst2.c:12: note: #pragma message: FOO NOT DEFINED
~/sandbox/tst6> gcc tst2.c -DFOO=
tst2.c:7: note: #pragma message: FOO DEFINED AS EMPTY
~/sandbox/tst6> gcc tst2.c -DFOO=1
tst2.c:9: note: #pragma message: FOO DEFINED (NOT EMPTY)

答案 1 :(得分:1)

你是正确的相信C预处理器,就像C编译器一样, 执行&& - 和|| - 表达式的短路评估。

但是,与编译器不同,你认为预处理器是错误的, 可以对无意义的令牌序列进行“短路”评估, 只要废话处于上下文之一:

TRUE || [nonsense]

FALSE && [nonsense]

就像编译器一样,预处理器可以评估的任何序列 表达式,短路与否,必须是(格式良好的)表达, 这

FALSE && ( == 0)

不是。

答案 2 :(得分:0)

所有#if条件都已通过,因为KERNEL_PATCH_LEVEL 定义。它只是被定义为一个空字符串。 -DKERNEL_PATCH_LEVEL=相当于

#define KERNEL_PATCH_LEVEL

当然,will pass an #if (defined) test

somewhat difficult来测试宏是否为空。如果版本字符串中没有特殊值(如0或-1),修改makefile以定义KERNEL_PATCH_LEVEL可能更容易。