RabbitMQ C++客戶端SimpleAmqpClient編譯總結(32以及64位)
整個過程沒有截圖,使用為我的“印象筆記”不能傳圖,所以全是以文字形式描述,各位跟蹤我的腳步,一般能走到末尾。
遇到的問題:
1、Cmake必須是2.8版本或以上
2、boost編譯方式必須是預設動態編譯庫,使用靜態庫編譯會報錯(最終我重新編譯boost庫使用完全且預設動態方式:b2 toolset=msvc-14.0 threading=multi );
3、安裝vs2015不完全,且編譯vs2015要選擇“ Visual Studio 14 2015 Win64”,而不是選擇“ Visual Studio 15 2017 Win64”(指的是vs2017版本)
4、cmake-gui選擇source code為“D:/soft/MQ/SimpleAmqpClient”,選擇編譯二進位制路徑“D:/soft/MQ/SimpleAmqpClient/build”
5、配置後configure,選擇visual staudio 14 2015後,然後報錯找不到boost庫,需要配置BOOST_ROOT或BOOST_LIBRARY,這裡直接點選按鈕《Add Entry》然後新增BOOST_ROOT,選擇環境變數型別為PATH,對應值為boost編譯的路徑,我這裡選擇的是“D:\boost_1_63_0”
編譯過程:
1、下載SimpleAmqpClient
解壓SimpleAmqpClient到: D:\soft\MQ\SimpleAmqpClient
2、安裝boost庫
這裡我一直使用的是boost1.63,這裡需要注意的是不能使用boost編譯的st,也就是靜態庫,否則cmake編譯報錯,編譯boost:
(1)進入boost解壓目錄,執行:bootstrap.bat,出現b2.exe和bjam.exe可執行編譯工具
(2)開啟命令列,進入到boost目錄:cmd-->cd D:\boost_1_63_0-->D:
(3)編譯boost庫:b2 toolset=msvc-14.0 threading=multi,需要1個小時左右
3、CMake-gui編譯
(1)安裝cmake的windows版本,選擇下載的版本在2.8以上
(2)開啟cmake,選擇SimpleAmqpClient中cmake.list所在的目錄路徑為原始碼路徑
(3)設定編譯後路徑,在(2)路徑基礎後新增build即可
(4)選擇configure按鈕,選中visual studio 14 2015,確定後選擇Add Entry新增環境變數設定BOOST_ROOT,型別為PATH,值為D:\boost_1_63_0
(5)選擇Generate按鈕開始生成。報錯找不到 Required library Rabbitmqc NOT FOUND 說明SimpleAmqpClient依賴rabbitmq-c原始碼和庫,故需要編譯rabbitmq-c庫
4、編譯rabbitmq-c
(3)解壓rabbitmq-c和rabbitmq-codegen,並將rabbitmq-codegen解壓後重命名為codegen放到rabbitmq-c(原來已經有一個空codegen目錄,覆蓋即可)
(4)開啟CMake-gui,設定原始碼目錄:D:/soft/MQ/rabbitmq-c-0.9.0,設定編譯庫目錄:D:/soft/MQ/rabbitmq-c-0.9.0/build
(5)configure編譯選擇vs2015,報錯:Could NOT find OpenSSL,找不到openssl,這裡我們不需要,去掉中間環境變數鍵值對的ENABLE_OPENSSL_SUPPORT後的複選框去掉;如果需要的話增加環境變數條目:OPENSSL_ROOT_DIR,設定對應路徑即可;
擴充套件:
不需要配置環境變數能自動掃描到openssl,但是生成的工程編譯realse報錯;
a. 錯誤:error LNK2019: 無法解析的外部符號 __iob_func
解決方法:在rabbitmq工程檔案amqp_api.c檔案中上方標頭檔案包含目錄下新增如下程式碼:
// VS2015下解決32位系統編譯錯誤:error LNK2019: 無法解析的外部符號 __iob_func
#if _MSC_VER>=1900
#include "stdio.h"
_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#ifdef __cplusplus
extern "C"
#endif
FILE* __cdecl __iob_func(unsigned i) {
return __acrt_iob_func(i);
}
#endif /* _MSC_VER>=1900 */
b. 錯誤:error LNK2019: 無法解析的外部符號 __vsnwprintf
在linker中新增依賴庫:legacy_stdio_definitions.lib
c. 錯誤:error LNK2019: 無法解析的外部符號 [email protected]
openssl靜態庫缺少密碼學庫,在linker中新增依賴庫:Crypt32.lib
(6)再次點選configure,然後點選Generate即可
(7)使用vs2015開啟build目錄下的工程目錄,全部編譯即可生成21個專案庫;
庫路徑生成在build目錄:build/librabbitmq/Debug/librabbitmq.4.lib
5、編譯SimpleAmqpClient
(1)已經解決第三步中我們出現錯誤,開啟CMake選擇SimpleAmqpClient的原始碼目錄和庫目錄;
原始碼目錄:D:/soft/MQ/SimpleAmqpClient
編譯目錄:D:/soft/MQ/SimpleAmqpClient/build
(2)設定boost庫目錄:新增entry環境變數,設定path值為,BOOST_ROOT:D:\boost_1_63_0
(3)配置編譯configure,選擇vs2015,然後選擇Generate生成vs2015解決方案;
(4)報錯,找不到rabbitmq-c庫,新增編譯選項(rabbitmqc標頭檔案目錄和庫目錄-點選Add Entry按鈕):
Rabbitmqc_INCLUDE_DIR 路徑值:D:/soft/MQ/rabbitmq-c-0.9.0/librabbitmq
Rabbitmqc_LIBRARY 路徑值:D:/soft/MQ/rabbitmq-c-0.9.0/build/librabbitmq/Release/librabbitmq.4.lib
(5)重新configure(兩次紅色消失),然後generate即可生成工程檔案,vs2015開啟解決方案
(6)編譯報錯,原因是因為依賴openssl庫,將vs2015依賴的openssl配置到工程SimpleAmpqClient屬性中:
vc++目錄:
包含目錄:C:\openssl_lib\win32-release\include
庫目錄 :C:\openssl_lib\win32-release\lib
聯結器:輸入新增openssl和密碼學庫以及vs2015連線靜態庫bug解決庫:
libssl.lib
libcrypto.lib
Crypt32.lib
legacy_stdio_definitions.lib
(7)同樣配置Realse和其他版本,然後選擇對應版本(如Release版本)編譯即可一次通過編譯!
最終在build/Release(根據選擇定)目錄下生成SimpleAmqpClient.2.dll和SimpleAmqpClient.2.lib
6、編譯64
(1)編譯openssl64位庫
(2)編譯64位動靜態庫rabbitmq-c-0.9.0,設定cmake的庫輸出路徑:D:/soft/MQ/rabbitmq-c-0.9.0/build64
(3)編譯64位impleAmqpClient動靜態庫,設定cmake的庫輸出路徑:D:/soft/MQ/SimpleAmqpClient/build64
關於64位版本使用情況(cmake選擇Visual Studio 14 2015 Win64,編譯32bit則選擇Visual Studio 14 2015):
(1)編譯64位版本的openssl 1.1.x行不通-32位的可以,具體見我的文章,openssl1.0.x編譯和openssl1.1.x編譯的步驟和區別,所以此處更改為openssl1.0.x版本
(2)編譯好後的openssl不是標準目錄結構,標準目標是openssl的根目錄下有include和lib兩個資料夾,所以需要將openssl的inc32改為include,將out32改lib(用32為則改out32為lib,有空64則改out64為lib)
(3)boost編譯輸出到與32預設不同的stage/lib,目錄,SimpleAmqpClient編譯必須重新在聯結器中設定boost庫的目錄,或臨時更改成stage/lib(32的改成其他)--在linker的lib目錄中修改,輸入中也得修改;
(4)SimpleAmqpClient的依賴庫必須設定openssl的庫重新設定libssl.lib、libcrypto.lib、Crypt32.lib然後重新編譯;
最後附上我最終編譯出來的靜態庫連結,包含了rabbitmq-0.9.0c的靜態庫和openssl靜態庫以及對應的32和64的版本:
後增:為了解決SimpleAmqpClient.2.dll跨元件記憶體釋放問題,解決方法如下:
由於SimpleAmqpClient封裝的庫用到了string等變數作為匯出類介面,如果編譯成DLL,在主程序呼叫改DLL的
時候,會出現元件記憶體申請與釋放問題,會有如下情況發生:
(1)如果SimpleAmqpClient.DLL的多執行緒型別為/MD,主程序為/MD,那麼DLL記憶體與主程序記憶體用的是主進行的內容,不存在這邊申請,那邊釋放問題,所以程序不會出現崩潰問題;
(2)如果SimpleAmqpClient.DLL的多執行緒型別為/MT,則要求SimpleAmqpClient匯出類不能有string等基本型別庫,但是實際程式碼卻已經寫死,不便修改,MT程序呼叫MD會崩潰;
(3)如果SimpleAmqpClient為MT,主程序為MT,那麼按道理說不會存在崩潰,但是實際測試結果:主程序為Debug模式,出現斷言錯誤,確定後仍然不會有問題,主程序為Release模式則正常;
為了避免(1)和(2)的問題,解決方法是:
(1)主程序或呼叫SimpleAmqpClient.DLL程序多執行緒都設定為MT且為Release(測試可以通過且無錯誤提示框蹦出來-->header==xxx斷言)
(2)主程序或呼叫程序為MD且只能呼叫多執行緒也為MD的SimpleAmqpClient.DLL,這種情況會導致主程序依賴很多vc庫檔案
(3)把SimpleAmqpClient編譯成靜態庫的MT,然後主程序呼叫
動態庫使用include標頭檔案宣告,靜態庫使用include-static裡的標頭檔案,原因件最後
使用方式:
(1)動態庫
// 標準庫
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <string>
using namespace std;
// Boost庫
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/unordered_map.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/date_time.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/chrono.hpp>
#include <boost/thread/condition.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/shared_array.hpp>
#include <SimpleAmqpClient/SimpleAmqpClient.h>
#pragma comment(lib, "SimpleAmqpClient.2.lib")
(2)靜態庫【重點注意,不是新增標頭檔案就可以了,32bit編譯不通過,64bit沒有問題,原因是dll工程轉static庫工程問題】
// 標準庫
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <string>
using namespace std;
// Boost庫
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/unordered_map.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/date_time.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/chrono.hpp>
#include <boost/thread/condition.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/shared_array.hpp>
#include <SimpleAmqpClient/SimpleAmqpClient.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
#pragma comment(lib, "librabbitmq.4.lib")
#pragma comment(lib, "SimpleAmqpClient.2.lib")
重點問題:
dll工程轉static工程方法:
(1)將專案-->屬性-->常規-->配置型別(父項:專案預設值)更改為靜態庫lib
(2)將專案-->屬性-->常規-->目標副檔名(父項:常規)更改為.lib
(3)重點在這裡:將dll類或函式的匯出宣告去掉,隨意找到一個類,跳轉到匯出巨集定義宣告,這裡找到的定義在
\src\SimpleAmqpClient\Util.h
(4)更改巨集定義為:
#ifdef WIN32
#ifdef SimpleAmqpClient_EXPORTS
#define SIMPLEAMQPCLIENT_EXPORT /*__declspec(dllexport)*/
#else
#define SIMPLEAMQPCLIENT_EXPORT /*__declspec(dllimport)*/
#endif
#else
#define SIMPLEAMQPCLIENT_EXPORT
#endif
(5)最後編譯生成靜態庫即可,否則報錯提示dllimport,改關鍵字僅僅適用於dll,也就是存在改關鍵字會讓vs誤以為是動態庫匯入,所以不會以靜態庫方式匯入,這樣就會發生變異錯誤,提示找不到匯出來建構函式。