使用 yacc/bison 和 tab.h 最佳实践的外源构建

问题描述

我正在尝试对使用 GNU bison & flex 的项目使用源外构建进行解析和词法分析。

Build 由 GNU Make 管理,一切都很顺利,直到我将逻辑从主 .y 文件分离到一个新的 .c 文件。 Makefile 来自 this post

主要问题是 .tab.h 是由 bison 生成的,它是在 build 目录中生成的:./build/src/parser.tab.h

通过使用相对路径 .tab.h 包含 #include "../build/src/parser.tab.h" 并将 .tab.c 添加到 C 文件的依赖项,我设法以临时方式解决了这个问题。

这被认为是一个好习惯吗? 有没有办法在 Makefile 中隐式声明这一点和/或包含生成.tab.h 文件

这是我的 C 文件

#include "../build/src/parser.tab.h"

int main(int argc,char *argv[])
{
    yyparse();
    return 0;
}

Makefile

TARGET_EXEC := parser

BUILD_DIR := ./build
SRC_Dirs := ./src

SRCS := $(shell find $(SRC_Dirs) -name *.c -or -name *.y -or -name *.l)
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)

INC_Dirs := $(shell find $(SRC_Dirs) -type d)
INC_FLAGS := $(addprefix -I,$(INC_Dirs))

CC := gcc
CFLAGS := -O0 -Wall -Wextra -Wpedantic -std=c17
CPPFLAGS := $(INC_FLAGS) -MMD -MP
LDFLAGS := -ly -ll

YACC := bison
YFLAGS := -d

LEX := flex
LFLAGS :=

$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
    $(CC) $(OBJS) -o $@ $(LDFLAGS)

$(BUILD_DIR)/%.c.o: %.c build/src/parser.tab.c
    mkdir -p $(dir $@)
    $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

%.y.o: %.tab.c
    mkdir -p $(dir $@)
    $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

%.l.o: %.yy.c
    mkdir -p $(dir $@)
    $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

$(BUILD_DIR)/%.tab.c: %.y
    mkdir -p $(dir $@)
    $(YACC) $(YFLAGS) $< -o $@

$(BUILD_DIR)/%.yy.c: %.l
    mkdir -p $(dir $@)
    $(LEX) $(LFLAGS) -o $@ $<

.PHONY: clean
clean:
    rm -r $(BUILD_DIR)

-include $(DEPS)

这里是 MWE 词法分析器和解析器:

%{
#include "parser.tab.h"
#include <stdio.h>
%}
ws  [ \t]+
%%
{ws}    { ; }   // skip whitespaces
.   { printf("unkNown token %c\n",yytext[0]); }
%%
prgm: ;

前后树:

.
├── Makefile
├── build
│   ├── parser
│   └── src
│       ├── lexer.l.d
│       ├── lexer.l.o
│       ├── lexer.yy.c
│       ├── main.c.d
│       ├── main.c.o
│       ├── parser.tab.h
│       ├── parser.y.d
│       └── parser.y.o
└── src
    ├── lexer.l
    ├── main.c
    └── parser.y
.
├── Makefile
└── src
    ├── lexer.l
    ├── main.c
    └── parser.y

解决方法

首先,您的 makefile 比实际需要的更令人困惑,因为您在某些地方使用了 $(BUILD_DIR) 变量,而在其他地方使用了硬编码的 build:在任何地方都使用该变量。

>

第二,不,您不应该在源文件中包含路径。这意味着每当您更改 makefile 以移动某些内容时,您也必须编辑源文件。

相反,只需将搜索头文件的路径添加到编译器命令行即可。您已经有一个 INC_FLAGS 变量,其中包含告诉编译器在哪里查找头文件的选项;只需添加一个新的:

INC_FLAGS := $(addprefix -I,$(INC_DIRS)) -I$(BUILD_DIR)/src

现在您可以在源代码中使用 #include "y.tab.h"