1. 程式人生 > >RabbitMQ C++客戶端SimpleAmqpClient編譯總結(32以及64位)

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誤以為是動態庫匯入,所以不會以靜態庫方式匯入,這樣就會發生變異錯誤,提示找不到匯出來建構函式。