在上一篇文章中咱们介绍了使用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。