使用 clang++ 使用 SCons 编译 Antlr4 cpp 运行时。结果库给了我未定义的符号(macos)

Compiling Antlr4 cpp runtime with SCons using clang++. Resulting library gives me undefined symbols (macos)

我正在尝试制作一个 SConstruct,它将...

全部在 Macos (Catalina) 上使用 Clang++

现在我不得不说我不是 C++ 专家,所以我可能遗漏了一些非常明显的东西......希望如此。

我遇到的问题是第一步和最后一步。 我可以构建 Antlr4 运行时,但稍后它不会 link 与最终程序。简单地调用 #include "antlr4-runtime.h" 会导致 Undefined symbols for architecture x86_64 错误。

奇怪的是,如果我使用 cmake 构建 Antlr4 运行时,如 Antlr 存储库中所述,那么 linking 问题就会消失。

所以我用 cmake 构建它并使用 VERBOSE 模式查看 make 如何构建各种目标文件并确保我使用与 Scons 相同的标志

cmake/make 的输出示例:

clang++ 
-Iantlr4-master/runtime/Cpp/runtime/src 
-Iantlr4-master/runtime/Cpp/runtime/src/atn 
-Iantlr4-master/runtime/Cpp/runtime/src/dfa 
-Iantlr4-master/runtime/Cpp/runtime/src/misc 
-Iantlr4-master/runtime/Cpp/runtime/src/support 
-Iantlr4-master/runtime/Cpp/runtime/src/tree 
-Iantlr4-master/runtime/Cpp/runtime/src/tree/pattern 
-Iantlr4-master/runtime/Cpp/runtime/src/tree/xpath  
-Wall -pedantic -W -std=c++11 -stdlib=libc++ -O3 -DNDEBUG -O3 -DNDEBUG  
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk   
-Wno-overloaded-virtual -Wno-dollar-in-identifier-extension -Wno-four-char-constants  -std=gnu++11 
-o CMakeFiles/antlr4_static.dir/src/atn/PredicateTransition.cpp.o 
-c antlr4-master/runtime/Cpp/runtime/src/atn/PredicateTransition.cpp

显然这应该是一行,但为了更容易阅读,我分成了多行 ;)

所以我抓取了所有这些标志并将它们放入一个字符串中,让 Scons 弄清楚如何将它们应用到环境中:

pf = env.ParseFlags('\
    -Wall -pedantic -W -O3 -std=c++11 -stdlib=libc++ -DNDEBUG -std=gnu++11 \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
    -Wno-overloaded-virtual -Wno-dollar-in-identifier-extension -Wno-four-char-constants')
env.MergeFlags(pf)

现在我用 SCons 再次构建了 Antlr4,这是 SCons 创建的命令示例:

clang++ 
-o antlr4-master/runtime/Cpp/runtime/src/atn/PredicateTransition.o 
-c 
-std=c++11 -stdlib=libc++ -Wall -pedantic -W -O3 
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk 
-Wno-overloaded-virtual -Wno-dollar-in-identifier-extension -Wno-four-char-constants -DNDEBUG 
-Iantlr4-master/runtime/Cpp/runtime/src 
antlr4-master/runtime/Cpp/runtime/src/atn/PredicateTransition.cpp

我再次分成多行只是为了便于阅读...

我注意到的第一件事是命令中参数的顺序在 SCons 中无处不在,但其余部分几乎相同。 不幸的是,这给了我同样的 Undefined symbols for architecture x86_64 错误。
可能你注意到我只包含了 antlr4 运行时的主目录,这应该可以工作,因为所有的包含都从那个目录开始......无论如何,我也尝试像 cmake/make 那样包含所有子文件夹,结果是一样的。

我现在真的是一头雾水。希望你们中的一些人能够为我指明正确的方向。

这是我的 SConstruct 文件(路径与示例不同。但应该无关紧要)

import os
import subprocess


env = Environment(
    CC = "clang",
    CXX = "clang++",
    CXXFLAGS = ['-std=c++11', '-stdlib=libc++'],
    LINKFLAGS = ['-stdlib=libc++'],
)

env.Append(ENV = {'CLASSPATH': './dependencies/antlr4/antlr-4.8-complete.jar'})

#
# Builder for generating grammar files with antlr4 (the java app)
#

def antlr_emitter(target, source, env):
    root = os.path.splitext(source[0].name)[0]

    target  = ['./src/Parser/{}{}.cpp'.format(root, p) for p in (
        'BaseListener',
        'BaseVisitor',
        'Lexer',
        'Listener',
        'Parser',
        'Visitor'
    )]
    return target, source


AntlrBuilder = Builder(
    action='java org.antlr.v4.Tool $SOURCE -Dlanguage=Cpp -package Coral -visitor -listener -o src/Parser',
    emitter=antlr_emitter
)

env.Append(BUILDERS={'Antlr': AntlrBuilder})


#
# Cloning the Environment for Antlr4 runtime
#

antlrEnv = env.Clone()

pf = antlrEnv.ParseFlags('\
    -Wall -pedantic -W -O3 -DNDEBUG -std=gnu++11 \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
    -Wno-overloaded-virtual -Wno-dollar-in-identifier-extension -Wno-four-char-constants')
antlrEnv.MergeFlags(pf)


#
# Building in 3 steps, first the antlr4 runtime library, then the parser files with Antlr then the final program
#

AntlrRuntime = antlrEnv.Library(
                            'antlr4-runtime',
                            source = 
                                Glob('./dependencies/antlr4Runtime/*.cpp') + \
                                Glob('./dependencies/antlr4Runtime/atn/*.cpp') + \
                                Glob('./dependencies/antlr4Runtime/dfa/*.cpp') + \
                                Glob('./dependencies/antlr4Runtime/misc/*.cpp') + \
                                Glob('./dependencies/antlr4Runtime/support/.cpp') + \
                                Glob('./dependencies/antlr4Runtime/tree/*.cpp') + \
                                Glob('./dependencies/antlr4Runtime/tree/pattern/*.cpp') + \
                                Glob('./dependencies/antlr4Runtime/tree/xpath/*.cpp'),
                            CPPPATH = './dependencies/antlr4Runtime',
                            ARFLAGS = 'qc'
                          )

ParserFiles = env.Antlr(source='Coral.g4')

CoralLang = env.Program(target   = 'coral', 
                        source   = Glob('./src/*.cpp') + Glob('./src/Parser/*.cpp'), 
                        CPPPATH  = ['./src', './dependencies/antlr4Runtime'],
                        LIBPATH  = '.',
                        LIBS     = 'antlr4-runtime'
                        )



Requires(ParserFiles, AntlrRuntime)
Depends(CoralLang, ParserFiles)

Clean(ParserFiles, Glob('./src/Parser/*'))

最后是 main.cpp 文件。如您所见,它什么都没有,只是对 antlr 运行时的包含,这足以看到错误。

#include <cstdio>
#include "antlr4-runtime.h"

int main(int argc, const char *argv[])
{
    if (!argv[1]) {
        printf("hey! pass at least a file to parse...");
        return 0;
    }
    else {
        printf("parsing %s\n", argv[1]);
    }

    return 1;
}

尝试构建具有相对错误的程序时,整个 SCons 的输出是这样的:

clang++ -o src/Parser/CoralParser.o -c -std=c++11 -stdlib=libc++ -Isrc -Idependencies/antlr4Runtime src/Parser/CoralParser.cpp
clang++ -o src/Parser/CoralBaseVisitor.o -c -std=c++11 -stdlib=libc++ -Isrc -Idependencies/antlr4Runtime src/Parser/CoralBaseVisitor.cpp
clang++ -o src/Parser/CoralListener.o -c -std=c++11 -stdlib=libc++ -Isrc -Idependencies/antlr4Runtime src/Parser/CoralListener.cpp
clang++ -o src/Parser/CoralVisitor.o -c -std=c++11 -stdlib=libc++ -Isrc -Idependencies/antlr4Runtime src/Parser/CoralVisitor.cpp
clang++ -o src/Parser/CoralBaseListener.o -c -std=c++11 -stdlib=libc++ -Isrc -Idependencies/antlr4Runtime src/Parser/CoralBaseListener.cpp
clang++ -o src/Parser/CoralLexer.o -c -std=c++11 -stdlib=libc++ -Isrc -Idependencies/antlr4Runtime src/Parser/CoralLexer.cpp
clang++ -o coral -stdlib=libc++ src/main.o src/Parser/CoralBaseListener.o src/Parser/CoralBaseVisitor.o src/Parser/CoralLexer.o src/Parser/CoralListener.o src/Parser/CoralParser.o src/Parser/CoralVisitor.o -L. -lantlr4-runtime
Undefined symbols for architecture x86_64:
  "Guid::Guid(unsigned short const*, bool)", referenced from:
      antlr4::atn::ATNDeserializer::deserialize(std::__1::vector<unsigned short, std::__1::allocator<unsigned short> > const&) in libantlr4-runtime.a(ATNDeserializer.o)
      antlr4::atn::ATNDeserializer::toUUID(unsigned short const*, unsigned long) in libantlr4-runtime.a(ATNDeserializer.o)
  "Guid::Guid(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", referenced from:
      antlr4::atn::ATNDeserializer::ADDED_PRECEDENCE_TRANSITIONS() in libantlr4-runtime.a(ATNDeserializer.o)
      antlr4::atn::ATNDeserializer::ADDED_LEXER_ACTIONS() in libantlr4-runtime.a(ATNDeserializer.o)
      antlr4::atn::ATNDeserializer::ADDED_UNICODE_SMP() in libantlr4-runtime.a(ATNDeserializer.o)
      antlr4::atn::ATNDeserializer::SERIALIZED_UUID() in libantlr4-runtime.a(ATNDeserializer.o)
      antlr4::atn::ATNDeserializer::BASE_SERIALIZED_UUID() in libantlr4-runtime.a(ATNDeserializer.o)
      antlr4::atn::ATNDeserializer::SUPPORTED_UUIDS() in libantlr4-runtime.a(ATNDeserializer.o)
      antlr4::atn::ATNDeserializer::deserialize(std::__1::vector<unsigned short, std::__1::allocator<unsigned short> > const&) in libantlr4-runtime.a(ATNDeserializer.o)
      ...
  "Guid::Guid(Guid const&)", referenced from:
      antlr4::atn::ATNDeserializer::SUPPORTED_UUIDS() in libantlr4-runtime.a(ATNDeserializer.o)
  "antlr4::ANTLRErrorListener::~ANTLRErrorListener()", referenced from:
      antlr4::Recognizer::Recognizer() in libantlr4-runtime.a(Recognizer.o)
      antlr4::Recognizer::~Recognizer() in libantlr4-runtime.a(Recognizer.o)
      antlr4::ProxyErrorListener::~ProxyErrorListener() in libantlr4-runtime.a(ProxyErrorListener.o)
      antlr4::ProxyErrorListener::~ProxyErrorListener() in libantlr4-runtime.a(ProxyErrorListener.o)
      antlr4::ConsoleErrorListener::~ConsoleErrorListener() in libantlr4-runtime.a(ConsoleErrorListener.o)
      antlr4::ConsoleErrorListener::~ConsoleErrorListener() in libantlr4-runtime.a(ConsoleErrorListener.o)
      antlr4::BaseErrorListener::~BaseErrorListener() in libantlr4-runtime.a(BaseErrorListener.o)
      ...
  "antlrcpp::replaceAll(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", referenced from:
      antlr4::Recognizer::getTokenErrorDisplay(antlr4::Token*) in libantlr4-runtime.a(Recognizer.o)
      antlr4::DefaultErrorStrategy::escapeWSAndQuote(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const in libantlr4-runtime.a(DefaultErrorStrategy.o)
      antlr4::CommonToken::toString(antlr4::Recognizer*) const in libantlr4-runtime.a(CommonToken.o)
  "antlrcpp::arrayToString(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&)", referenced from:
      antlr4::ParserRuleContext::toInfoString(antlr4::Parser*) in libantlr4-runtime.a(ParserRuleContext.o)
  "antlrcpp::escapeWhitespace(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool)", referenced from:
      antlr4::LexerNoViableAltException::toString() in libantlr4-runtime.a(LexerNoViableAltException.o)
      antlr4::tree::Trees::toStringTree(antlr4::tree::ParseTree*, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, bool) in libantlr4-runtime.a(Trees.o)
  "antlrcpp::SingleWriteMultipleReadLock::readUnlock()", referenced from:
      antlr4::atn::LexerATNSimulator::getExistingTargetState(antlr4::dfa::DFAState*, unsigned long) in libantlr4-runtime.a(LexerATNSimulator.o)
      antlr4::atn::ParserATNSimulator::getExistingTargetState(antlr4::dfa::DFAState*, unsigned long) in libantlr4-runtime.a(ParserATNSimulator.o)
  "antlrcpp::SingleWriteMultipleReadLock::writeUnlock()", referenced from:
      antlr4::atn::LexerATNSimulator::addDFAEdge(antlr4::dfa::DFAState*, unsigned long, antlr4::dfa::DFAState*) in libantlr4-runtime.a(LexerATNSimulator.o)
      antlr4::atn::LexerATNSimulator::addDFAState(antlr4::atn::ATNConfigSet*) in libantlr4-runtime.a(LexerATNSimulator.o)
      antlr4::atn::ParserATNSimulator::adaptivePredict(antlr4::TokenStream*, unsigned long, antlr4::ParserRuleContext*) in libantlr4-runtime.a(ParserATNSimulator.o)
      antlr4::atn::ParserATNSimulator::addDFAEdge(antlr4::dfa::DFA&, antlr4::dfa::DFAState*, long, antlr4::dfa::DFAState*) in libantlr4-runtime.a(ParserATNSimulator.o)
      antlr4::dfa::DFA::setPrecedenceStartState(int, antlr4::dfa::DFAState*, antlrcpp::SingleWriteMultipleReadLock&) in libantlr4-runtime.a(DFA.o)
  "antlrcpp::SingleWriteMultipleReadLock::readLock()", referenced from:
      antlr4::atn::LexerATNSimulator::getExistingTargetState(antlr4::dfa::DFAState*, unsigned long) in libantlr4-runtime.a(LexerATNSimulator.o)
      antlr4::atn::ParserATNSimulator::getExistingTargetState(antlr4::dfa::DFAState*, unsigned long) in libantlr4-runtime.a(ParserATNSimulator.o)
  "antlrcpp::SingleWriteMultipleReadLock::writeLock()", referenced from:
      antlr4::atn::LexerATNSimulator::addDFAEdge(antlr4::dfa::DFAState*, unsigned long, antlr4::dfa::DFAState*) in libantlr4-runtime.a(LexerATNSimulator.o)
      antlr4::atn::LexerATNSimulator::addDFAState(antlr4::atn::ATNConfigSet*) in libantlr4-runtime.a(LexerATNSimulator.o)
      antlr4::atn::ParserATNSimulator::adaptivePredict(antlr4::TokenStream*, unsigned long, antlr4::ParserRuleContext*) in libantlr4-runtime.a(ParserATNSimulator.o)
      antlr4::atn::ParserATNSimulator::addDFAEdge(antlr4::dfa::DFA&, antlr4::dfa::DFAState*, long, antlr4::dfa::DFAState*) in libantlr4-runtime.a(ParserATNSimulator.o)
      antlr4::dfa::DFA::setPrecedenceStartState(int, antlr4::dfa::DFAState*, antlrcpp::SingleWriteMultipleReadLock&) in libantlr4-runtime.a(DFA.o)
  "antlrcpp::toMap(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&)", referenced from:
      antlr4::Recognizer::getRuleIndexMap() in libantlr4-runtime.a(Recognizer.o)
  "antlrcpp::indent(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)", referenced from:
      antlr4::atn::ATN::toString() const in libantlr4-runtime.a(ATN.o)
  "antlrcpp::finally(std::__1::function<void ()>)", referenced from:
      Coral::CoralParser::unit() in CoralParser.o
      Coral::CoralParser::assignment() in CoralParser.o
      Coral::CoralParser::variable() in CoralParser.o
      Coral::CoralParser::number() in CoralParser.o
      Coral::CoralParser::expression(int) in CoralParser.o
      antlr4::atn::LexerATNSimulator::match(antlr4::CharStream*, unsigned long) in libantlr4-runtime.a(LexerATNSimulator.o)
      antlr4::atn::LexerATNSimulator::evaluatePredicate(antlr4::CharStream*, unsigned long, unsigned long, bool) in libantlr4-runtime.a(LexerATNSimulator.o)
      ...
  "Guid::toString() const", referenced from:
      antlr4::atn::ATNDeserializer::deserialize(std::__1::vector<unsigned short, std::__1::allocator<unsigned short> > const&) in libantlr4-runtime.a(ATNDeserializer.o)
  "Guid::operator==(Guid const&) const", referenced from:
      antlr4::atn::ATNDeserializer::isFeatureSupported(Guid const&, Guid const&) in libantlr4-runtime.a(ATNDeserializer.o)
      antlr4::atn::ATNDeserializer::deserialize(std::__1::vector<unsigned short, std::__1::allocator<unsigned short> > const&) in libantlr4-runtime.a(ATNDeserializer.o)
  "typeinfo for antlr4::ANTLRErrorListener", referenced from:
      typeinfo for antlr4::ProxyErrorListener in libantlr4-runtime.a(ProxyErrorListener.o)
      typeinfo for antlr4::BaseErrorListener in libantlr4-runtime.a(BaseErrorListener.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

根据缺少的符号输出,您需要至少再添加一个库到您的 LIBS。 无论提供什么:

   Guid::*
   antlr4::*
   antlrcpp::*

你可以试试运行

ldd libantlr-runtime.so

要查看它希望link反对哪个库?