在 Windows 上构建 C++ 项目时,Tensorflow 2.3 无法解析机器生成的文件中的外部符号
Posted
技术标签:
【中文标题】在 Windows 上构建 C++ 项目时,Tensorflow 2.3 无法解析机器生成的文件中的外部符号【英文标题】:Tensorflow 2.3 unresolved external symbols in machine-generated files when building C++ project on Windows 【发布时间】:2020-07-17 13:07:17 【问题描述】:我想知道是否有人在 Windows 上成功链接了 Tensorflow 2.3 DLL。 我正在尝试在一个小型 VS2019 测试项目中集成一些 tensorflow 功能,看看我是否可以使用 tensorflow C++ API。
我设法使用 MSVC 14.16.27023 构建了 tensorflow,并按照官方链接中给出的说明进行操作: https://docs.bazel.build/versions/master/windows.html#build-c https://www.tensorflow.org/install/source_windows 我使用以下 Bazel 命令构建了 .dll 和 .lib:bazel build --config=opt //tensorflow:tensorflow.dll
bazel build --config=opt //tensorflow:tensorflow.lib
VS2019项目属性:
添加相关Additional Include Directories
添加Additional Library Directories
,也就是path-to-tensorflow-source\bazel-bin\tensorflow
,其中bazel-bin
目录是构建系统生成的(即Bazel)
在Linker->Input->Additional Dependencies
添加生成的tensorflow.lib
源代码很小,由我在 Joe Antognini 的 example 中查找的几行代码组成:
#pragma once
#define NOMINMAX
#include "tensorflow/core/public/session.h"
#include "tensorflow/cc/ops/standard_ops.h"
int main()
tensorflow::Scope root = tensorflow::Scope::NewRootScope();
auto X = tensorflow::ops::Placeholder(root.WithOpName("x"), tensorflow::DT_FLOAT,
tensorflow::ops::Placeholder::Shape( -1, 2 ));
正如 Ashley Tharp 的 guide 中所述,立即构建会导致链接器错误,并抱怨未解析的外部符号:
LNK2001 无法解析的外部符号“private: class tensorflow::Scope __cdecl tensorflow::Scope::WithOpNameImpl(... LNK2001 未解析的外部符号“public: static class tensorflow::Scope __cdecl tensorflow::Scope::NewRootScope(void)”... LNK2001 未解析的外部符号“public: __cdecl tensorflow::Scope::~Scope(void)”... LNK2001 未解析的外部符号“public: __cdecl tensorflow::ops::Placeholder::Placeholder(...这是她的导游的解释:
发生这种情况的原因是您只能在 dll 中公开 60,000 个符号。这只是 dll 格式的一些限制。 tensorflow 库代码有超过 60000 个符号,因此作为构建此 dll 的程序员(dll 只是用于在运行时访问库的二进制文件),您将必须手动指示要公开的符号(如果它们尚未公开)。 Google 选择了一些默认设置,但它可能并不适合所有人。
并且,按照指南中的建议,我进入了 tensorflow 标头,并在相关符号前添加了 TF_EXPORT 宏,这是您常用的 DLL 导入-导出模式:
#ifdef TF_COMPILE_LIBRARY
#define TF_EXPORT __declspec(dllexport)
#else
#define TF_EXPORT __declspec(dllimport)
#endif // TF_COMPILE_LIBRARY
上述解决方法适用于前 3 个错误:
// scope.h
~Scope(); // change to: TF_EXPORT ~Scope();
...
static Scope NewRootScope(); // change to: TF_EXPORT static Scope NewRootScope();
...
Scope WithOpNameImpl(const string& op_name) const; // change to TF_EXPORT Scope...
完成编辑后,我像bazel build --config=opt //tensorflow:tensorflow.lib
那样重新运行bazel,错误消失了。
但是,当我尝试执行类似的操作来修复最后一个未解析的符号时,问题就出现了,即Placeholder
。该符号位于array_ops.h
,这是一个机器生成的文件。因此,一旦我尝试构建 .lib,我在文件中编辑的任何内容都会被覆盖并丢失。
问题:如何公开出现在机器生成文件中的符号?非常感谢您指出正确方向的指针,也许我遗漏了一些明显的东西。
【问题讨论】:
好问题,干得好! +1 【参考方案1】:包含动态库tensorflow_cc.dll和导入库tensorflow_cc.dll.if.lib的文件夹也有两个文件:
tensorflow_filtered_def_file.def:包含导入符号 tensorflow_cc.dll-2.params :拥有所有内置库现在,如果您在构建应用程序时有一些未解析的符号,您所要做的就是使用更新的 tensorflow_filtered_def_file.def 文件重建动态库。对于此文件,您必须添加缺少的符号,对于您的示例代码,它是:
EXPORTS
??1Scope@tensorflow@@QEAA@XZ
?NewRootScope@Scope@tensorflow@@SA?AV12@XZ
??0Placeholder@ops@tensorflow@@QEAA@AEBVScope@2@W4DataType@2@AEBUAttrs@012@@Z
?WithOpNameImpl@Scope@tensorflow@@AEBA?AV12@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z
the rest symbols ...
更新.def文件后,调用以下命令重建dll:
link.exe /nologo /DLL /SUBSYSTEM:CONSOLE -defaultlib:advapi32.lib -DEFAULTLIB:advapi32.lib
-ignore:4221 /FORCE:MULTIPLE /MACHINE:X64
@bazel-out/x64_windows-opt/bin/tensorflow/tensorflow_cc.dll-2.params /OPT:ICF /OPT:REF
/DEF:bazel-out/x64_windows-opt/bin/tensorflow/tensorflow_filtered_def_file.def /ignore:4070
在重建 dll/lib 之前删除这些文件(它们只是只读的)。
更多信息here,请参阅 rafix07 评论。
【讨论】:
以上是关于在 Windows 上构建 C++ 项目时,Tensorflow 2.3 无法解析机器生成的文件中的外部符号的主要内容,如果未能解决你的问题,请参考以下文章
使用 CMake、Clang 和 Ninja 在 Windows 上构建 c++ 项目
buildbot C++ 在 Windows 上构建:使用 devenv.com、vcbuild.exe 还是 MSBuild.exe?
如何在 Windows 上使用 Visual C++ 强制加载链接库
如何在 Windows 10 上使用 Visual Studio 2015 x64 配置和构建 Tesseract OCR C++