在上一篇文章中咱們介紹了使用vs2019做爲遠程Linux系統的開發環境,但咱們是建立的傳統的sln項目,而對於Linux開發者來講以autotools或是cmake進行項目結構的組織更爲簡單直觀,也符合在Linux環境上的習慣。html
autotools是較爲古老的也是使用最爲普遍的構建系統,你在Linux上老是避免不了相似./configure && make
這樣的命令,背後就是autotools爲你完成了檢測系統環境到生成makefile的一系列工做。linux
cmake是較新的一種工具,autotools雖然功能強大使用普遍,可是它的學習成本和維護成本也十分驚人,因此人們創造了cmake來簡化工做。cmake十分簡單易學,在表現力上絲絕不亞於autotools,同時還提供了豐富的官方模塊和第三方模塊以便於定製各類各樣的功能。已經有許多項目開始使用cmake了,例如google test框架,qbittorrent,KDE,_MySQL_等,將來Qt也會從qmake遷移至cmake,目前已經提供了初步支持。ios
遺憾的是vs2019並不支持autotools工具鏈,可是vs2019支持cmake,並且相比vs2017,vs2019提供了遠程開發的cmake支持,而且支持了更多的設置選項,因此咱們今天將會介紹如何使用vs2019+cmake實現Linux遠程開發。不過須要注意的是,本文是介紹如何搭建開發環境的,並不會介紹cmake的語法,而且我也假設各位讀者已經基本瞭解了簡單的CMkaeLists.txt該如何編寫,若是不瞭解那麼你可能須要先進行簡單的cmake學習,這超出了本文的討論範圍你能夠尋找其餘的博客園文章學習相關知識。固然,即便理解不了後文所羅列的CMakeLists.txt的內容也不要緊,我會盡可能給出簡單易懂的註釋。c++
好了,如今該讓咱們進入主題了。shell
建立很簡單,在vs的啓動窗口中選擇「建立新項目」,而後找到「CMkae項目」,選擇後點擊下一步便可,和建立傳統項目的過程徹底同樣,如圖:json
建立完成後你的項目裏會是以下的場景(假如項目名稱叫CMakeProject1):緩存
也許你會奇怪,爲何cmake項目不像sln項目那樣區分出Linux和Windows平臺呢?答案是咱們能夠經過對項目進行設置來切換本地環境和遠程環境!app
整個項目由CMakeLists.txt進行組織,而vs則負責在什麼環境上運行cmake,這樣就實現了同一套項目能夠幾乎不通過修改在不一樣平臺上編譯運行(只要你的目標平臺裝有cmake,且版本最低爲3.8;本地環境vs自帶了cmake)。框架
默認狀況下的cmake project是在本地環境的,因此接下來咱們建立一個叫「LinuxQt」的遠程項目,接着設置對應的遠程Linux環境。dom
設置遠程環境以前,你須要先在頂部的工具菜單的選項對話框中將遠程鏈接設置好,並同步遠程環境的頭文件,具體過程能夠參考這篇,過程同樣就不贅述了。
在初始的項目中啓動項要麼是某個文件要麼是空的,沒有咱們的遠程環境,因此咱們須要右鍵資源管理器中顯示的CMakeLists.txt文件:
找到「project-name的CMake設置」,project-name是你的項目名稱,點擊。這時會生成一個「CMakeSettings.json」的文件,這是整個項目的配置文件,雙擊打開會顯示圖形化的配置界面:
首先咱們看到了配置名稱,這是給你的自定義配置起名字的地方,右邊的綠色加號表示添加新的配置,由於咱們只想使用Linux遠程環境,因此咱們直接修改了默認的配置項。
接下來是配置類型,這和cmake中的選項對應,在此處設置後就無需再寫進CMakeLists.txt了,有Debug,Release等模式,咱們選擇Release,由於遠程環境上的Qt我沒有安裝調試符合,選Debug除了增大編譯目標的體積外也沒什麼用。
下面則是重點,遠程計算機名稱選項。點擊下拉框便可出現咱們在鏈接管理器中添加的遠程環境,若是你沒有添加遠程環境,在右側的按鈕能夠直接打開鏈接管理器進行添加。該選項默認是空的,也就是本機編譯不啓用遠程環境。
接下來是工具集,也就是最終調用的編譯器工具鏈,vs支持gcc和clang,linux_x64
對應gcc,linux_clang_x64
對應clang,此外還有arm平臺的支持,選用什麼工具鏈看對應平臺和我的喜愛,我這裏選擇了gcc。
而後是「遠程生成根」這個選項,截圖裏未給出,這是遠程編譯時vs存放整個項目的路徑,默認在你的家目錄下的.vs
目錄裏,你也能夠根據本身的須要修改這一路徑,咱們演示用的項目就直接使用默認值了。
生成根選項後是設置調用cmake程序時的參數的,只要把須要的參數原樣填入輸入框便可,這裏咱們沒用到也就不截圖了。
vs2019中一個強大的功能就是能夠把cmake中由系統或是模塊產生的變量的值顯示出來(須要在cache成功刷新以後,也就是cmakelists文件保存後或手動在項目菜單中單擊爲項目生成緩存):
接着咱們點擊顯示高級選項,由於想要vs能提供代碼補全還須要一點設置:
在這裏你能夠設置cmake生成什麼類型的makefile,cmake的運行目錄和編譯完成後程序的安裝目錄,以及cmake自己所在的路徑(若是你把cmake安裝到了不太常規的地方例如/opt)。
其中重點關注IntellSense選項,這是選擇代碼補全的引擎:
能夠看到全部選項都是由平臺名稱-編譯器名稱-32位/64位
這種格式組成的,默認值是空,咱們想要代碼補全可用就要選擇和遠程環境徹底對應的那種模式。
另外右上角一直有直接編輯json文件的按鈕,若是你討厭gui的話能夠選擇它。
最後咱們保存修改,vs會自動刷新cache,如今咱們能夠進行遠程開發了。
前面說過cmake項目的組織須要依靠CMakeLists.txt,如今咱們來編寫它。
咱們的測試項目會使用Qt,隨機顯示一些不一樣引擎產生的隨機數,而後把它們顯示在圖表中。選擇這個示例是爲了更好的展現cmake項目的能力,可是遠程開發gui程序在vs上目前還有些困難:
雖然有以上的缺陷,可是咱們編寫單個文件的項目而且不自定義widget,同時只編譯生成程序而不運行的話仍是沒有問題的。
下面來看看CMakeLists.txt是如何編寫的:
project(LinuxQtExample) # 設置c++語言標準,我使用c++17 set(CMAKE_CXX_STANDARD 17) cmake_minimum_required (VERSION 3.10) set(CMAKE_INCLUDE_CURRENT_DIR ON) # 自動調用moc, uic, rcc set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) # 找到這些Qt組件 find_package(Qt5Widgets REQUIRED) find_package(Qt5Core REQUIRED) find_package(Qt5Gui REQUIRED) find_package(Qt5Charts REQUIRED) # 將源代碼添加到此項目的可執行文件。 add_executable (LinuxQt "main.cpp") # 將Qt的庫連接至程序 target_link_libraries(LinuxQt Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Charts)
更多如何用cmake構建Qt程序的內容請移步這裏。
上述設置結束後就能夠着手編寫代碼了,代碼提示和補全也能工做了(雖然對於Qt的部分補全不正常,可是c++標準庫的補全是能夠正常工做的):
#include <QApplication> #include <QBarCategoryAxis> #include <QBarSet> #include <QBarSeries> #include <QChart> #include <QChartView> #include <QPushButton> #include <QString> #include <QStringList> #include <QValueAxis> #include <QVBoxLayout> #include <iostream> #include <random> // 這個函數裏變量名起的很爛,由於是示例我偷懶了,請你不要在實際項目中寫出這種代碼 // 建立柱狀圖數據的函數 // std::random_device的某些實如今Windows上存在bug,每次運行會返回一樣的結果序列,linux沒問題 // QtCharts的全部類型/函數都在對應的命名空間中,和其餘的QtWidgets不一樣 static QtCharts::QBarSeries* createSeries() { auto dataSet1 = new QtCharts::QBarSet("mt19937"); auto seed = std::random_device{}(); std::uniform_int_distribution<int> u(0, 100); std::mt19937 rd1(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd1); std::cout << a << std::endl; *dataSet1 << a; } auto dataSet2 = new QtCharts::QBarSet("minstd_rand"); std::minstd_rand rd2(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd2); std::cout << a << std::endl; *dataSet2 << a; } auto dataSet3 = new QtCharts::QBarSet("default"); std::default_random_engine rd3(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd3); std::cout << a << std::endl; *dataSet3 << a; } auto dataSet4 = new QtCharts::QBarSet("ranlux48"); std::ranlux48 rd4(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd4); std::cout << a << std::endl; *dataSet4 << a; } auto dataSet5 = new QtCharts::QBarSet("knuth_b"); std::knuth_b rd5(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd5); std::cout << a << std::endl; *dataSet5 << a; } auto barSeries = new QtCharts::QBarSeries; barSeries->append(dataSet1); barSeries->append(dataSet2); barSeries->append(dataSet3); barSeries->append(dataSet4); barSeries->append(dataSet5); return barSeries; } int main(int argc, char* argv[]) { QApplication app(argc, argv); auto chart = new QtCharts::QChart; // 建立Y軸顯示數據 auto axisY = new QtCharts::QValueAxis; axisY->setRange(0, 100); axisY->setTickCount(10); axisY->setTitleText("Y軸"); chart->addAxis(axisY, Qt::AlignLeft); // x軸顯示10次取隨機數的結果 QStringList x; for (int i = 0; i < 10; ++i) { x << QString::number(i+1); } auto axisX = new QtCharts::QBarCategoryAxis; axisX->append(x); chart->addAxis(axisX, Qt::AlignBottom); auto barSeries = createSeries(); chart->addSeries(barSeries); chart->setTitle("隨機數分佈圖"); // 顯示圖例以及讓圖例擺放在圖表的底部 chart->legend()->setVisible(true); chart->legend()->setAlignment(Qt::AlignBottom); // 顯示chart的容器 auto view = new QtCharts::QChartView(chart); view->setRenderHint(QPainter::Antialiasing); auto layout = new QVBoxLayout; layout->addWidget(view); // 點擊按鈕刷新顯示的數據 auto button = new QPushButton("點擊刷新"); QObject::connect(button, &QPushButton::clicked, [chart]() { // removeAll會幫你刪除原來的series,因此沒必要擔憂內存泄漏 chart->removeAllSeries(); auto barSeries = createSeries(); chart->addSeries(barSeries); }); layout->addWidget(button, Qt::AlignCenter); auto window = new QWidget; window->setLayout(layout); window->setWindowTitle("圖表"); // 圖表默認會顯示成最小,爲了避免讓圖表縮成一團須要給一個固定的大小 window->resize(700, 500); window->show(); app.exec(); }
代碼中使用了utf8編碼的中文字符串,你須要設置源文件的編碼爲utf8以避免在Linux上運行時出現亂碼。具體見這裏。
如以前所說,咱們不能直接點擊運行按鈕,因此對於gui程序咱們只能選擇頂部工具欄的生成->所有生成,這樣vs會自動調用cmake和make來完成程序的構建:
能夠看到vs將整個項目用rsync同步到了遠程機上,接着運行了cmake和make。
生成成功後咱們到以前設置的「遠程生成根」下out/build/...
,省略號表示的是你的cmake項目配置的名字,編譯好的程序就在這裏,下面在遠程環境中運行:
cmake項目整體上比sln更簡單也更好控制,只是細節上還有欠缺。
cmake本省也簡單易學,有着強大的功能,若是你是從Linux上的開發環境遷移至Windows不妨試一試cmake。