Windows 环境TensorFlow源码C++编译———实战与避坑记(3)

4.4.TensorFlow C++版开发库编译

4.4.1.  release编译

4.4.1.1.  编译tensorflow_cc.dll

bazel build --config=opt --define=no_tensorflow_py_deps=true --copt=-DTHRUST_IGNORE_CUB_VERSION_CHECK --copt=-nvcc_options=disable-warnings --local_ram_resources=10240 --local_cpu_resources=3  //tensorflow:tensorflow_cc.dll

4.4.1.2. 编译tensorflow_cc.lib

bazel build --config=opt --define=no_tensorflow_py_deps=true --copt=-DTHRUST_IGNORE_CUB_VERSION_CHECK --copt=-nvcc_options=disable-warnings --local_ram_resources=10240 --local_cpu_resources=3  //tensorflow:tensorflow_cc.lib

4.4.1.3.  编译include

bazel build --config=opt --define=no_tensorflow_py_deps=true --copt=-DTHRUST_IGNORE_CUB_VERSION_CHECK --copt=-nvcc_options=disable-warnings --local_ram_resources=10240 --local_cpu_resources=3  //tensorflow:install_headers

4.4.2. debug编译

编译时,通过添加”-c dbg”来实现debug版的编译。编译过程中遇到问题较多,未能成功编译。
bazel build  -c dbg --config=opt --define=no_tensorflow_py_deps=true --copt=-DTHRUST_IGNORE_CUB_VERSION_CHECK --copt=-nvcc_options=disable-warnings --local_ram_resources=10240 --local_cpu_resources=3   //tensorflow:tensorflow_cc.dll

4.4.3.编译结果

编译结果位于bazel_bin中,bazel_bin实际是一个软链接,真实数据存储在C:\Users\stone\_bazel_stone\7t3zwoz4\execroot\org_tensorflow

 

将编译结果整理到Tensorflow的安装目录下:

 

 

 

 

4.4.4. 【坑】C++开发时报缺少外部符号错误的解决办法

 

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::Subtract::Subtract(class tensorflow::Scope const &,class tensorflow::Input,class tensorflow::Input)" (??0Subtract@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::Squeeze::Squeeze(class tensorflow::Scope const &,class tensorflow::Input)" (??0Squeeze@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::DecodeGif::DecodeGif(class tensorflow::Scope const &,class tensorflow::Input)" (??0DecodeGif@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::TopK::TopK(class tensorflow::Scope const &,class tensorflow::Input,class tensorflow::Input)" (??0TopK@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::ExpandDims::ExpandDims(class tensorflow::Scope const &,class tensorflow::Input,class tensorflow::Input)" (??0ExpandDims@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::ResizeBilinear::ResizeBilinear(class tensorflow::Scope const &,class tensorflow::Input,class tensorflow::Input)" (??0ResizeBilinear@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "class tensorflow::Output __cdecl tensorflow::ops::Const(class tensorflow::Scope const &,struct tensorflow::Input::Initializer const &)" (?Const@ops@tensorflow@@YA?AVOutput@2@AEBVScope@2@AEBUInitializer@Input@2@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::DecodePng::DecodePng(class tensorflow::Scope const &,class tensorflow::Input,struct tensorflow::ops::DecodePng::Attrs const &)" (??0DecodePng@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@AEBUAttrs@012@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::Placeholder::Placeholder(class tensorflow::Scope const &,enum tensorflow::DataType)" (??0Placeholder@ops@tensorflow@@QEAA@AEBVScope@2@W4DataType@2@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::Cast::Cast(class tensorflow::Scope const &,class tensorflow::Input,enum tensorflow::DataType)" (??0Cast@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@W4DataType@2@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::DecodeJpeg::DecodeJpeg(class tensorflow::Scope const &,class tensorflow::Input,struct tensorflow::ops::DecodeJpeg::Attrs const &)" (??0DecodeJpeg@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@AEBUAttrs@012@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::DecodeBmp::DecodeBmp(class tensorflow::Scope const &,class tensorflow::Input)" (??0DecodeBmp@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::Div::Div(class tensorflow::Scope const &,class tensorflow::Input,class tensorflow::Input)" (??0Div@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::Operation::Operation(class tensorflow::Node *)" (??0Operation@tensorflow@@QEAA@PEAVNode@1@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "private: class tensorflow::Scope __cdecl tensorflow::Scope::WithOpNameImpl(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)const " (?WithOpNameImpl@Scope@tensorflow@@AEBA?AV12@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: class tensorflow::Status __cdecl tensorflow::Scope::ToGraphDef(class tensorflow::GraphDef *)const " (?ToGraphDef@Scope@tensorflow@@QEBA?AVStatus@2@PEAVGraphDef@2@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: static class tensorflow::Scope __cdecl tensorflow::Scope::NewRootScope(void)" (?NewRootScope@Scope@tensorflow@@SA?AV12@XZ)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::Scope::~Scope(void)" (??1Scope@tensorflow@@QEAA@XZ)

1>main.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::Input::Initializer::Initializer(class std::initializer_list<struct tensorflow::Input::Initializer> const &)" (??0Initializer@Input@tensorflow@@QEAA@AEBV?$initializer_list@UInitializer@Input@tensorflow@@@std@@@Z)

1>main.obj : error LNK2001: 无法解析的外部符号 "class tensorflow::Status __cdecl tensorflow::NewSession(struct tensorflow::SessionOptions const &,class tensorflow::Session * *)" (?NewSession@tensorflow@@YA?AVStatus@1@AEBUSessionOptions@1@PEAPEAVSession@1@@Z)

1>ModelLoader.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ops::ReadFile::ReadFile(class tensorflow::Scope const &,class tensorflow::Input)" (??0ReadFile@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z)

1>ModelLoader.obj : error LNK2001: 无法解析的外部符号 "public: class tensorflow::Status __cdecl tensorflow::ClientSession::Run(class std::vector<class tensorflow::Output,class std::allocator<class tensorflow::Output> > const &,class std::vector<class tensorflow::Tensor,class std::allocator<class tensorflow::Tensor> > *)const " (?Run@ClientSession@tensorflow@@QEBA?AVStatus@2@AEBV?$vector@VOutput@tensorflow@@V?$allocator@VOutput@tensorflow@@@std@@@std@@PEAV?$vector@VTensor@tensorflow@@V?$allocator@VTensor@tensorflow@@@std@@@5@@Z)

1>ModelLoader.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ClientSession::~ClientSession(void)" (??1ClientSession@tensorflow@@QEAA@XZ)

1>ModelLoader.obj : error LNK2001: 无法解析的外部符号 "public: __cdecl tensorflow::ClientSession::ClientSession(class tensorflow::Scope const &)" (??0ClientSession@tensorflow@@QEAA@AEBVScope@1@@Z)

整理后主要是下面这些符号:

       ??0Cast@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@W4DataType@2@@Z

    ?Const@ops@tensorflow@@YA?AVOutput@2@AEBVScope@2@AEBUInitializer@Input@2@@Z

    ??1ClientSession@tensorflow@@QEAA@XZ

    ??0ClientSession@tensorflow@@QEAA@AEBVScope@1@@Z

    ??0DecodeGif@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z

    ??0DecodePng@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@AEBUAttrs@012@@Z

    ??0DecodeJpeg@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@AEBUAttrs@012@@Z

    ??0DecodeBmp@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z

    ??0Div@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z

    ??0ExpandDims@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z

    ??0Initializer@Input@tensorflow@@QEAA@AEBV?$initializer_list@UInitializer@Input@tensorflow@@@std@@@Z

    ?NewRootScope@Scope@tensorflow@@SA?AV12@XZ

    ?NewSession@tensorflow@@YA?AVStatus@1@AEBUSessionOptions@1@PEAPEAVSession@1@@Z

    ??0Operation@tensorflow@@QEAA@PEAVNode@1@@Z

    ??0ResizeBilinear@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z

    ??0ReadFile@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z

    ?Run@ClientSession@tensorflow@@QEBA?AVStatus@2@AEBV?$vector@VOutput@tensorflow@@V?$allocator@VOutput@tensorflow@@@std@@@std@@PEAV?$vector@VTensor@tensorflow@@V?$allocator@VTensor@tensorflow@@@std@@@5@@Z

    ??1Scope@tensorflow@@QEAA@XZ

    ??0Subtract@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z

    ??0Squeeze@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z

    ??0TopK@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z

    ??0Placeholder@ops@tensorflow@@QEAA@AEBVScope@2@W4DataType@2@@Z

    ?ToGraphDef@Scope@tensorflow@@QEBA?AVStatus@2@PEAVGraphDef@2@@Z

    ?WithOpNameImpl@Scope@tensorflow@@AEBA?AV12@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z

4.4.4.1.      【不推荐】方式一:声明TF_EXPORT

在相关类声明的头文件添加TF_EXPORT后,重新编译dll、lib和include。增加TF_EXPORT时需要视情添加头文件引用#include "tensorflow/core/platform/macros.h。
主要需修改的头文件有:
tensorflow/cc/client/client_session.h
tensorflow/cc/framework/scope.h
tensorflow/core/public/session.h
tensorflow/core/public/session_options.h 
tensorflow/cc/framework/ops.h
bazel-bin/tensorflow/cc/ops/ image_ops.h
bazel-bin/tensorflow/cc/ops/io_ops.h
bazel-bin/tensorflow/cc/ops/array_ops.h
bazel-bin/tensorflow/core/framework/graph.pb.h
bazel-bin/tensorflow/core/protobuf/meta_graph.pb.h
bazel-bin/tensorflow/core/protobuf/config.pb.h
bazel-bin/tensorflow/cc/ops/nn_ops.h
这种方式,修改的地方太多了,不推荐。

4.4.4.1.      方式二:修改def_file_filter.py.tpl文件

通过在tensorflow\tools\def_file_filter\def_file_filter.py.tpl文件中增加符号后,重新编译dll、lib和include。
特别注意格式,每行前面不能用TAB键,只能用4个空格,行尾不能有空格。
def_fp.write("\t ??0SessionOptions@tensorflow@@QEAA@XZ\n")
def_fp.write("\t ?NewSession@tensorflow@@YAPEAVSession@1@AEBUSessionOptions@1@@Z\n")
def_fp.write("\t ??1SavedModelBundleInterface@tensorflow@@UEAA@XZ\n")
def_fp.write("\t ?LoadSavedModel@tensorflow@@YA?AVStatus@1@AEBUSessionOptions@1@AEBVRunOptions@1@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV?$unordered_set@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@U?$hash@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@U?$equal_to@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@6@QEAUSavedModelBundle@1@@Z\n")
def_fp.write("\t ?MaybeSavedModelDirectory@tensorflow@@YA_NAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z\n")
def_fp.write("\t ?_TensorShapeProto_default_instance_@tensorflow@@3VTensorShapeProtoDefaultTypeInternal@1@A\n")
def_fp.write("\t ??0Cast@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@W4DataType@2@@Z\n")
def_fp.write("\t ?Const@ops@tensorflow@@YA?AVOutput@2@AEBVScope@2@AEBUInitializer@Input@2@@Z\n")
def_fp.write("\t ??1ClientSession@tensorflow@@QEAA@XZ\n")
def_fp.write("\t ??0ClientSession@tensorflow@@QEAA@AEBVScope@1@@Z\n")
def_fp.write("\t ??0DecodeGif@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z\n")
def_fp.write("\t ??0DecodePng@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@AEBUAttrs@012@@Z\n")
def_fp.write("\t ??0DecodeJpeg@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@AEBUAttrs@012@@Z\n")
def_fp.write("\t ??0DecodeBmp@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z\n")
def_fp.write("\t ??0Div@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z\n")
def_fp.write("\t ??0ExpandDims@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z\n")
def_fp.write("\t ??0Initializer@Input@tensorflow@@QEAA@AEBV?$initializer_list@UInitializer@Input@tensorflow@@@std@@@Z\n")
def_fp.write("\t ?NewRootScope@Scope@tensorflow@@SA?AV12@XZ\n")
def_fp.write("\t ?NewSession@tensorflow@@YA?AVStatus@1@AEBUSessionOptions@1@PEAPEAVSession@1@@Z\n")
def_fp.write("\t ??0Operation@tensorflow@@QEAA@PEAVNode@1@@Z\n")
def_fp.write("\t ??0ResizeBilinear@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z\n")
def_fp.write("\t ??0ReadFile@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z\n")
def_fp.write("\t ?Run@ClientSession@tensorflow@@QEBA?AVStatus@2@AEBV?$vector@VOutput@tensorflow@@V?$allocator@VOutput@tensorflow@@@std@@@std@@PEAV?$vector@VTensor@tensorflow@@V?$allocator@VTensor@tensorflow@@@std@@@5@@Z\n")
def_fp.write("\t ??1Scope@tensorflow@@QEAA@XZ\n")
def_fp.write("\t ??0Subtract@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z\n")
def_fp.write("\t ??0Squeeze@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@@Z\n")
def_fp.write("\t ??0TopK@ops@tensorflow@@QEAA@AEBVScope@2@VInput@2@1@Z\n")
def_fp.write("\t ??0Placeholder@ops@tensorflow@@QEAA@AEBVScope@2@W4DataType@2@@Z\n")
def_fp.write("\t ?ToGraphDef@Scope@tensorflow@@QEBA?AVStatus@2@PEAVGraphDef@2@@Z\n")
def_fp.write("\t ?WithOpNameImpl@Scope@tensorflow@@AEBA?AV12@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z\n")

4.4.4.3. 【推荐】方式三:修改tensorflow_filtered_def_file文件

在tensorflow_filtered_def_file.def文件中添加外部符号,然后利用link.exe重新生成dll、lib。
为了便于后续开发过程中遇到其他未知外部符号的解决,可以将tensorflow编译过程中生成的各个依赖lib文件收集在一处,然后修改tensorflow_cc.dll-2.params文件中每个lib的路径,最后使用link.exe重新生成dll和lib。
tensorflow_filtered_def_file.def需要增加的外部符号如下图:

 

tensorflow_cc.dll-2.params修改前后如下所示:

 

 

 

link.exe执行过程:
(1)以管理员权限启动VS命令行工具

 

(2)在命令行下定位到tensorflow_cc.dll-2.params和tensorflow_filtered_def_file.def所在目录。

 

(3)执行命令:link.exe @tensorflow_cc.dll-2.params

 

4.4.5.MFC测试TensorFlow

4.4.5.1.  新建MFC项目

新建MFC对话框程序,并添加按钮“TensorFlow测试”。

4.4.5.2. 为MFC程序开启命令行窗口

为了便于调试分析,为MFC程序开启命令行窗口。
在应用程序App类实现文件中添加下面的代码:

//增加下面这行代码,控制台输出信息会更多

#pragma comment( linker, "/subsystem:console /entry:wWinMainCRTStartup" )

 

#include<io.h>

#include<fcntl.h>

void InitConsole()

{

    ::AllocConsole();

    FILE* fp;

    freopen_s(&fp, "CONOUT$", "w+t", stdout);

}

在应用程序App类的初始化函数InitInstance中调用InitConsole函数初始化控制台资源。

 

在应用程序App类的退出函数ExitInstance中调用FreeConsole函数释放控制台资源。

 

4.4.5.3. 配置项目Release属性—头文件和库文件目录

由于我们编译的只有Release版的TensorFlow,因此在VS中只需要配置项目Release的属性即可。
加入tensorflow的include和lib目录。

为了使用方便,将编译好的tensorflow的安装目录配置为系统环境变量$(THIRDPARTY_HOME)。

 

4.4.5.4. 配置项目Release属性—加入预定义宏NOMINMAX

配置项目Release属性,在C/C++预处理器定义中加入NOMINMAX。

 

在MFC中调用TensorFlow时,会报错,主要是因为tensorflow调用的min,max函数是std命名空间下的,而MFC使用的min,max是Windows SDK提供的(头文件minwindef.h),这会导致下面的错误。

 

解决办法:在工程项目的预处理器中定义:NOMINMAX,这样MFC中就不会调用minwindef.h中的min,max宏。

4.4.5.5. 配置项目pch.h文件—为Gdiplus指定min,max

在pch.h的头文件中,在#include “framework.h”之前为gdiplus指定使用的min,max函数。
#include<algorithm>
namespace Gdiplus
{
using std::min;
using std::max;
}

注意:在项目属性配置时加入预定义宏NOMINMAX,会导致gdiplustypes.h中找不到min,max的错误出现:

查看minwindef.h文件中有明确的定义:只有在没有定义NOMINMAX宏的情况下,才会有min,max宏定义。

解决办法:在MFC所有头文件引用之前,为Gdiplus命名空间指定使用std::min和std::max函数。

4.4.5.6. 配置项目pch.h文件—引入tensorflow头文件和库文件

在pch.h的头文件中,在#include “framework.h”之后加入引入tensorflow的头文件和库文件的代码。
#pragma warning (disable: 4995)
#include <tensorflow/core/platform/env.h>
#include <tensorflow/core/public/session.h>

#pragma comment(lib,"tensorflow_cc.lib")

 

4.4.5.7. 编写测试代码

在对话框类中添加testtf函数。

void CTFTestDlg::testtf()

{

    tensorflow::Session* session;

    tensorflow::Status status = tensorflow::NewSession(tensorflow::SessionOptions(), &session);

    if (!status.ok()) {

        std::cout << status.ToString() << std::endl;

        return;

    }

    std::cout << "Session successfully created.\n";

}
在对话框类的按钮“TensorFlow测试”BN_CLICK消息响应函数中调用testtf()。

 

4.4.5.8. 编译程序的Release版

注意要选择Release项进行编译。

 

4.4.5.9. 执行测试程序

运行程序界面如下。

 

 

点击“Tensorflow测试”按钮后,命令行窗口出现“Session successfully created.”,测试通过。

 

 

后记:

    整个编译测试过程还是挺艰辛曲折的。如果有需要编译好的动态库和python安装包,可以私信我。

 本人第一次发博客,想请教一个问题:如何将word内容直接发布到博客园?现在已经可以在word中登录上博客,但是在发布时提示 “与博客提供商联系”,点击“取消”后,提示“word无法发布文章中的图片。产生此问题的原因可能是您的存储空间不够。”。应该如何才能将word文档内容正确发布到微博中呢? 有谁清楚的话,烦请告知,不胜感谢!

 

 

 

posted @ 2021-12-08 18:33  bluesky998  阅读(929)  评论(0)    收藏  举报