Thinking of a project without a CI/CD is unimaginable, even more for a cross-platform application where we have to juggle between multiple platforms. CI/CD Continuous integration (CI) and continuous delivery (CD) offer several advantages:
For Qt-based projects that are cross-platform by design, a CI/CD pipeline will have several additional benefits:
In the following, we will see how to set up an essential GitHub Actions CD to obtain binaries of our software for Windows, Linux, and macOS. In the second part, we will see how to produce Windows online and offline installer with the QtInstallerFrameWork.
To build our pipeline, we will use several tools. The main one is the GitHub Actions framework, Aqtinstall, a command-line installer for Qt, and qmake to compile the Qt program.
First, we create a folder “.github/workflows” inside the git repository of our project. Then create an action named “windows.yml”.
name: Build Windows
on:
push:
branches: [master]
Photo by Vidar Nordli-Mathisen on Unsplash
jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: ilammy/msvc-dev-cmd@v1
- uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: install qt6
run: |
pip install aqtinstall
python3 -m aqt install-qt -m qtwebengine qtwebchannel qtpositioning -O ${{ github.workspace }}/Qt/ windows desktop 6.2.0 win64_msvc2019_64
echo "${{ github.workspace }}/Qt/6.2.0/msvc2019_64/bin/" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: build
shell: cmd
run: |
cd src/
qmake6 Program.pro -spec win32-msvc
nmake release
nmake clean
cd release
windeployqt Program.exe --release --compiler-runtime
copy ..\assets\icon.ico .
copy C:\Windows\System32\concrt140.dll .
copy C:\Windows\System32\vccorlib140.dll .
copy C:\Windows\System32\msvcp140.dll .
copy C:\Windows\System32\vcruntime140.dll .
7z a C:\Program.zip *
- name: Windows artefact
uses: actions/upload-artifact@v1
with:
name: WindowsBuild
path: C:\Program.zip
First, we name the Actions (Build Windows) and select when it is triggered, in this case, at each commit on the master branch. Then we create a job and select the GitHub runner, the latest Windows available.
actions/checkout@v2 will clone the repository, and ilammy/msvc-dev-cmd@v1 will set up an environment where we can use nmake. Finally, actions/setup-python@v2 will set up a Python environment.
We use aqtinstall to install Qt on the machine. We install several modules (qtwebengine, qtwebchannel, qtpositioning) alongside the Qt version 6.2.0 for MSVC 2019 64 bits. Finally, we set the path to the Qt installation using the $env:GITHUB_PATH variable.
As usual, we build the program using qmake and nmake.
At this step, we have produced a single binary file. To deploy the application, we must copy the necessary libraries for the execution. We use the Qt tool name windeployqt. Be aware that this tool will not copy external (non-Qt) libraries; this step must be performed manually. Additionally, we manually copy several dll that must be redistributed to ensure the standalone execution (see https://doc.qt.io/qt-5/windows-deployment.html section Creating the Application Package).
Finally, we use actions/upload-artifact@v1 to upload the resulting folder as an Action artifact that we will be able to download.
macos.yml:
name: Build MacOs
on:
push:
branches: [master]
jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: install qt6
run: |
pip install aqtinstall
python3 -m aqt install-qt -m qtwebengine qtwebchannel qtpositioning -O ${{ github.workspace }}/Qt/ mac desktop 6.2.0
echo ${{ github.workspace }}/Qt/6.2.0/macos/bin/ >> $GITHUB_PATH
- name: build
run: |
qmake6 src/Program.pro
make
make clean
cd build/
macdeployqt Program.app -always-overwrite
wget https://raw.githubusercontent.com/arl/macdeployqtfix/master/macdeployqtfix.py
python2.7 macdeployqtfix.py Program.app/Contents/MacOS/Program ../../Qt/6.2.0/
hdiutil create -volname Program -srcfolder Program.app -ov -format UDZO Program.dmg
- name: Mac artefact
uses: actions/upload-artifact@v1
with:
name: MacOsBuild
path: ./build/Program.dmg
The procedure for MacOs is similar. At the end of the compilation, we use macdeployqt to copy the necessary libraries in the bundle and set the correct rpaths. For external dependencies, it is possible that macdeployqt doesn’t finish the job. To correct that, we use macdeployqtfix. Finally, we package the App as a dmg file.
There are several ways to release packages for Linux. In this example, we use the AppImage format that will be compatible with all Linux distributions.
linux.yml:
name: Build AppImage
on:
push:
branches: [master]
jobs:
job_1:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: install qt6
run: |
pip install aqtinstall
python3 -m aqt install-qt -m qtwebengine qtwebchannel qtpositioning -O ${{ github.workspace }}/Qt/ linux desktop 6.2.0
echo ${{ github.workspace }}/Qt/6.2.0/gcc_64/bin/ >> $GITHUB_PATH
- name: build
run: |
qmake6 src/Program.pro
make
make clean
- name: appimage
run: |
cd build
wget -O deploy.AppImage https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage
chmod +x deploy.AppImage
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${{ github.workspace }}/Qt/6.2.0/gcc_64/lib/
./deploy.AppImage Program -appimage -no-translations -bundle-non-qt-libs
mv Program*.AppImage Program-x86_64.AppImage
- name: Linux artefact
uses: actions/upload-artifact@v1
with:
name: LinuxBuild
path: ./build/Program-x86_64.AppImage
As for the other, the procedure is the same. We use linuxdeployqt to create the AppImage file at the end before uploading the file as an action artifact.
Setting up a CI/CD that will build Qt program on Linux, Windows, and macOS is not very complicated and will result in considerable time savings. In the next part of the Qt series, we will see how to create executable (offline and online) for Windows with the Qt Installer Framework and GitHub Actions.