In the
previous article
, we introduced the development environment using vs2019 as a remote Linux system, but we are creating a traditional sln project, and for Linux developers, the organization of project structure with autotools or cmake is simpler and more intuitive. It also conforms to the habits of the Linux environment.

Autotools is the oldest and most widely used build system. You can’t avoid ./configure && makecommands like this on Linux. The behind is autotools for you to complete a series of tests to detect the system environment to generate makefiles.

Cmake is a relatively new tool. Although autotools is powerful and widely used, its learning cost and maintenance cost are also amazing, so people created cmake to simplify the work. Cmake is very easy to learn, no less expressive than autotools, but also provides a wealth of official modules and third-party modules to customize a wide range of features. There have been many projects that have started using cmake, such as google test framework, qbittorrent, KDE, _MySQL_, etc. In the future, Qt will also migrate from qmake to cmake, which has provided initial support.

Unfortunately, vs2019 does not support the autotools toolchain, but vs2019 supports cmake, and compared to vs2017, vs2019 provides remote development cmake support and supports more setup options, so we will introduce how to use vs2019+cmake today. Realize Linux remote development. However, it should be noted that this article is to introduce how to build a development environment, and will not introduce the syntax of cmake, and I also assume that the readers have basically understood how to write simple CMkaeLists.txt. If you don’t understand, you may need to first Doing simple cmake learning is beyond the scope of this article. You can find other blogging articles to learn about it. Of course, even if you can’t understand the contents of CMakeLists.txt listed in the following article, I will try to give easy-to-understand comments.

Ok, now let’s get into the theme.

Create a remote cmake project

The creation is very simple. Select “Create New Project” in the startup window of vs, then find “CMkae Project”, select and click Next, and the process of creating traditional project is exactly the same, as shown in the figure:

After the creation is complete, your project will be the following scenario (if the project name is CMakeProject1):


Develop_app_for_Linux_using_Vs2019_and_cmake_1.jpg

 

Perhaps you will wonder why the cmake project does not distinguish between Linux and Windows platforms like the sln project. The answer is that we can switch between the local environment and the remote environment by setting the project!

The entire project is organized by CMakeLists.txt, and vs is responsible for running cmake on any environment, so that the same set of projects can be compiled and run on different platforms with almost no modification (as long as your target platform is loaded with cmake, and the version The minimum is 3.8; the local environment vs comes with cmake).

By default the cmake project is in the local environment, so next we create a remote project called “LinuxQt” and then set up the corresponding remote Linux environment.

Setting up a remote environment

Before setting up the remote environment, you need to set up the remote connection in the option dialog box of the top tool menu and synchronize the header files of the remote environment.

In the initial project the startup item is either a file or empty, without our remote environment, so we need to right click on the CMakeLists.txt file displayed in the Explorer:

Find “project-name CMake settings”, project-name is your project name, click. This will generate a “CMakeSettings.json” file, which is the configuration file for the entire project. Double-clicking will display the graphical configuration interface:

First we see the configuration name, this is the name given to your custom configuration, the green plus sign on the right indicates the addition of a new configuration, because we only want to use the Linux remote environment, so we directly modify the default configuration items.

Next is the configuration type, which corresponds to the options in cmake. After setting it here, there is no need to write CMakeLists.txt. There are Debug, Release and other modes. We choose Release because Qt on the remote environment is not installed and debugged. In line with, Debug is not useful except to increase the size of the compilation target.

Below is the focus, remote computer name options. Click on the drop-down box to bring up the remote environment we added in the connection manager. If you have not added the remote environment, the button on the right can be opened directly by adding the connection manager. This option is empty by default, which means that the native compilation does not enable the remote environment.

Next is the toolset, which is the final call to the compiler toolchain, vs. gcc and clang, linux_x64gcc, linux_clang_x64clang, and arm platform support. What toolchain to use to see the platform and personal preferences, I choose here Gcc.

Then there is the option of “remote generation root”, which is not shown in the screenshot. This is the path to save the entire project when compiling remotely. By default, in the directory under your home directory .vs, you can also modify this path according to your needs. The project we are using demonstrates the default value.

After generating the root option, set the parameters when calling the cmake program. Just fill in the required parameters as they are in the input box. We will not use the screenshot here.

A powerful feature in vs2019 is the ability to display the values ​​of variables generated by the system or module in cmake (after the cache is successfully refreshed, ie after the cmakelists file is saved or manually click on the project menu to generate a cache for the project) :

Then we click on the Show Advanced option, because you want a vs. code completion and you need a bit of setup:
Here you can set what type of makefile is generated by cmake, the running directory of cmake and the installation directory of the compiled program, and the path where cmake itself is located (if you installed cmake in a less conventional place such as /opt).

The focus is on the IntellSense option, which is the engine for choosing code completion:

It can be seen that all options are composed of  32bit/64bit this format, the default value is empty, we want to choose the mode that corresponds to the remote environment.

In addition, there is always a button to edit the json file directly in the upper right corner. If you hate gui, you can choose it.

Finally, we save the changes, vs will automatically refresh the cache, and now we can remotely develop.

Write CMakeLists.txt

As mentioned earlier, the organization of the cmake project needs to rely on CMakeLists.txt, now let’s write it.

Our test project will use Qt to randomly display random numbers generated by different engines and display them in the chart. This example was chosen to better demonstrate the capabilities of the cmake project, but remote development of the gui program is currently somewhat difficult on vs.

  1. The program running the remote environment relies on ssh. However, the Linux gui program needs to connect to the xserver (usually the connection information is in the environment variable). The shell environment started by ssh does not have these environment variables. You may need to set additional commands when the program starts. Line parameters, otherwise an error will occur during the run.
  2. This is the reason for Qt itself. Qt relies on its own moc system, which is somewhat different from native C++. Therefore, when the code is completed, it will often not find the type (clion has no such problem).
  3. Vs own problem, although Qt supports cmake itself, but vs. does not work properly when calling moc in remote environment, custom widget will report similar problems such as vtable.
  4. Qt vs tool cannot work in a remote environment.

Despite the above drawbacks, we write a single file project and don’t customize the widget, and only compile the build program without running it.

Let’s take a look at how CMakeLists.txt is written:

project(LinuxQtExample)

# Set the C++ language standard,c++17
set(CMAKE_CXX_STANDARD 17)

cmake_minimum_required (VERSION 3.10)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

# auto use moc, uic, rcc
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

# Find these Qt components
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Charts REQUIRED)

# Add source code to the executable for this project
add_executable (LinuxQt "main.cpp")

# Link Qt's library to the program
target_link_libraries(LinuxQt Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Charts)

Write test code

After the above settings are finished, you can start writing code, and the code hints and completions will work. (Although the partial completion of Qt is not normal, the completion of the C++ standard library can work normally):

#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>


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;

    // Create Y-axis display data
    auto axisY = new QtCharts::QValueAxis;
    axisY->setRange(0, 100);
    axisY->setTickCount(10);
    axisY->setTitleText("Y axis");
    chart->addAxis(axisY, Qt::AlignLeft);

    // The x-axis shows the result of taking random numbers 10 times.
    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("Random number distribution map");
    // Show the legend and place the legend at the bottom of the chart
    chart->legend()->setVisible(true);
    chart->legend()->setAlignment(Qt::AlignBottom);
    // Container showing chart
    auto view = new QtCharts::QChartView(chart);
    view->setRenderHint(QPainter::Antialiasing);

    auto layout = new QVBoxLayout;
    layout->addWidget(view);
    // Click the button to refresh the displayed data
    auto button = new QPushButton("Click refresh");
    QObject::connect(button, &QPushButton::clicked, [chart]() {
        // removeAll will help you delete the original series, so don't worry about memory leaks
        chart->removeAllSeries();
        auto barSeries = createSeries();
        chart->addSeries(barSeries);
    });
    layout->addWidget(button, Qt::AlignCenter);
    auto window = new QWidget;
    window->setLayout(layout);
    window->setWindowTitle("chart");
    // The chart will be displayed as a minimum by default, in order to prevent the chart from shrinking into a group, you need to give a fixed size.
    window->resize(700, 500);
    window->show();
    app.exec();
}

Running test

As mentioned before, we can’t just click the Run button, so for the gui program we can only select the build of the top toolbar -> all build, so vs will automatically call cmake and make to complete the program build:

You can see that vs. syncs the entire project to the remote machine with rsync, then runs cmake and make.

After the success is generated, we go to the “remote generation root” set before out/build/…, the ellipsis indicates the name of your cmake project configuration, the compiled program is here, and the following is run in the remote environment:

to sum up

The cmake project is generally simpler and better controlled than sln, but there is still a lack of detail.

Cmake is also easy to learn and has powerful features. If you are migrating from a Linux development environment to Windows, try cmake.