aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml37
-rw-r--r--.github/workflows/ci.yml77
-rw-r--r--.lgtm.yml18
-rw-r--r--CMakeLists.txt44
-rw-r--r--CONTRIBUTING.md24
-rw-r--r--README.md125
-rw-r--r--lib/jobs/basejob.cpp28
-rw-r--r--lib/jobs/basejob.h4
-rw-r--r--lib/quotient_common.h31
-rw-r--r--lib/room.cpp18
-rw-r--r--lib/room.h3
-rw-r--r--lib/util.cpp20
-rw-r--r--lib/util.h5
13 files changed, 233 insertions, 201 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index fa031ed8..00000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-image: Visual Studio 2017
-
-environment:
- CMAKE_ARGS: '-G "NMake Makefiles JOM" -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo'
- matrix:
- - QTDIR: C:\Qt\5.13\msvc2017 # Fresh Qt, 32-bit
- VCVARS: "vcvars32.bat"
- PLATFORM: x86
- - QTDIR: C:\Qt\5.13\msvc2017_64 # Fresh Qt, 64-bit
- VCVARS: "vcvars64.bat"
- PLATFORM:
-
-init:
-- call "%QTDIR%\bin\qtenv2.bat"
-- set PATH=C:\Qt\Tools\QtCreator\bin;%PATH%
-- call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\%VCVARS%" %PLATFORM%
-- cd /D "%APPVEYOR_BUILD_FOLDER%"
-
-before_build:
-- git submodule update --init --recursive
-
-build_script:
-- cmake %CMAKE_ARGS% -H. -Bbuild
-- cmake --build build
-
-#after_build:
-#- cmake --build build --target install
-#- 7z a quotient.zip "%DEPLOY_DIR%\"
-
-# Uncomment this to connect to the AppVeyor build worker
-#on_finish:
-# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
-
-test: off
-
-#artifacts:
-#- path: quotient.zip
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 20100e5f..47e31d55 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,13 +18,27 @@ jobs:
matrix:
os: [ubuntu-20.04, macos-10.15]
compiler: [ GCC, Clang ]
+ platform: [ '' ]
qt-version: [ '5.12.10' ]
+ qt-arch: [ '' ]
# Not using binary values here, to make the job captions more readable
e2ee: [ '' ]
update-api: [ '', 'update-api' ]
exclude:
- os: macos-10.15
compiler: GCC
+ include:
+ - os: windows-2019
+ compiler: MSVC
+ platform: x64
+ qt-version: '5.12.10'
+ qt-arch: win64_msvc2017_64
+ - os: windows-2019
+ compiler: MSVC
+ platform: x64
+ qt-version: '5.12.10'
+ qt-arch: win64_msvc2017_64
+ update-api: update-api
steps:
- uses: actions/checkout@v2
@@ -36,15 +50,16 @@ jobs:
uses: actions/cache@v2
with:
path: ${{ runner.workspace }}/Qt
- key: ${{ runner.os }}-Qt${{ matrix.qt-version }}-cache
+ key: ${{ runner.os }}${{ matrix.platform }}-Qt${{ matrix.qt-version }}-cache
- name: Install Qt
uses: jurplel/install-qt-action@v2.11.1
with:
version: ${{ matrix.qt-version }}
+ arch: ${{ matrix.qt-arch }}
cached: ${{ steps.cache-qt.outputs.cache-hit }}
- - name: Install Ninja (macOS)
+ - name: Install Ninja (macOS/Windows)
if: ${{ !startsWith(matrix.os, 'ubuntu') }}
uses: seanmiddleditch/gha-setup-ninja@v3
@@ -57,12 +72,17 @@ jobs:
- name: Setup build environment
run: |
if [ "${{ matrix.compiler }}" == "GCC" ]; then
- if [ -n "${{ matrix.update-api }}" ]; then VERSION_POSTFIX='-9'; fi
- echo "CC=gcc$VERSION_POSTFIX" >>$GITHUB_ENV
- echo "CXX=g++$VERSION_POSTFIX" >>$GITHUB_ENV
- else
- echo "CC=clang" >>$GITHUB_ENV
- echo "CXX=clang++" >>$GITHUB_ENV
+ CXX_VERSION_POSTFIX='-10'
+ echo "CC=gcc$CXX_VERSION_POSTFIX" >>$GITHUB_ENV
+ echo "CXX=g++$CXX_VERSION_POSTFIX" >>$GITHUB_ENV
+ elif [[ '${{ matrix.compiler }}' == 'Clang' ]]; then
+ if [[ '${{ runner.os }}' == 'Linux' ]]; then
+ CXX_VERSION_POSTFIX='-11'
+ # Do CodeQL analysis on one of Linux branches
+ echo "CODEQL_ANALYSIS=true" >>$GITHUB_ENV
+ fi
+ echo "CC=clang$CXX_VERSION_POSTFIX" >>$GITHUB_ENV
+ echo "CXX=clang++$CXX_VERSION_POSTFIX" >>$GITHUB_ENV
fi
if grep -q 'refs/tags' <<<'${{ github.ref }}'; then
VERSION="$(git describe --tags)"
@@ -76,10 +96,16 @@ jobs:
-DCMAKE_INSTALL_PREFIX=~/.local -DCMAKE_PREFIX_PATH=~/.local" >>$GITHUB_ENV
cmake -E make_directory ${{ runner.workspace }}/build
+ - name: Setup MSVC environment
+ uses: ilammy/msvc-dev-cmd@v1
+ if: matrix.compiler == 'MSVC'
+ with:
+ arch: ${{ matrix.platform }}
+
- name: Build and install olm
if: matrix.e2ee
run: |
- cd ${{ runner.workspace }}
+ cd ..
git clone https://gitlab.matrix.org/matrix-org/olm.git
cmake -S olm -B olm/build $CMAKE_ARGS
cmake --build olm/build --target install
@@ -88,18 +114,35 @@ jobs:
- name: Pull CS API and build GTAD
if: matrix.update-api
run: |
- cd ${{ runner.workspace }}
+ cd ..
git clone https://github.com/matrix-org/matrix-doc.git
git clone --recursive https://github.com/KitsuneRal/gtad.git
- cmake -S gtad -B gtad $CMAKE_ARGS
+ cmake -S gtad -B gtad $CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF
cmake --build gtad
- echo "CMAKE_ARGS=$CMAKE_ARGS -DMATRIX_DOC_PATH=${{ runner.workspace }}/matrix-doc \
- -DGTAD_PATH=${{ runner.workspace }}/gtad/gtad" \
+ echo "CMAKE_ARGS=$CMAKE_ARGS -DMATRIX_DOC_PATH=$GITHUB_WORKSPACE/../matrix-doc \
+ -DGTAD_PATH=$GITHUB_WORKSPACE/../gtad/gtad" \
>>$GITHUB_ENV
echo "QUOTEST_ORIGIN=$QUOTEST_ORIGIN and API files regeneration" >>$GITHUB_ENV
+
+ - name: Initialize CodeQL tools
+ if: env.CODEQL_ANALYSIS
+ uses: github/codeql-action/init@v1
+ with:
+ languages: cpp
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Configure libQuotient
- run: cmake -S $GITHUB_WORKSPACE -B build $CMAKE_ARGS -DQuotient_ENABLE_E2EE=${{ matrix.e2ee }}
+ run: |
+ if [[ '${{ runner.os }}' == 'Windows' ]]; then
+ BIN_DIR=.
+ else
+ BIN_DIR=bin
+ fi
+ echo "BIN_DIR=$BIN_DIR" >>$GITHUB_ENV
+ cmake -S $GITHUB_WORKSPACE -B build $CMAKE_ARGS -DQuotient_ENABLE_E2EE=${{ matrix.e2ee }}
- name: Regenerate API code
if: matrix.update-api
@@ -108,7 +151,7 @@ jobs:
- name: Build and install libQuotient
run: |
cmake --build build --target install
- ls ~/.local/bin/quotest
+ ls ~/.local/$BIN_DIR/quotest
- name: Run tests
env:
@@ -117,3 +160,7 @@ jobs:
run: |
[[ -z "$TEST_USER" ]] || $VALGRIND build/quotest/quotest "$TEST_USER" "$TEST_PWD" quotest-gha '#quotest:matrix.org' "$QUOTEST_ORIGIN"
timeout-minutes: 5 # quotest is supposed to finish within 3 minutes, actually
+
+ - name: Perform CodeQL analysis
+ if: env.CODEQL_ANALYSIS
+ uses: github/codeql-action/analyze@v1
diff --git a/.lgtm.yml b/.lgtm.yml
index f6dfb229..308675a8 100644
--- a/.lgtm.yml
+++ b/.lgtm.yml
@@ -6,15 +6,13 @@ path_classifiers:
extraction:
cpp:
prepare:
- packages: # Assuming package base of cosmic
- - ninja-build
- - qt5-default
+ packages: # Assuming package base of eoan
- qtmultimedia5-dev
- after_prepare:
- - git clone https://gitlab.matrix.org/matrix-org/olm.git
- - pushd olm
- - cmake . -Bbuild -GNinja
- - cmake --build build
- - popd
+# after_prepare:
+# - git clone https://gitlab.matrix.org/matrix-org/olm.git
+# - pushd olm
+# - cmake . -Bbuild -GNinja
+# - cmake --build build
+# - popd
configure:
- command: "cmake . -GNinja -DOlm_DIR=olm/build"
+ command: "cmake . -GNinja" # -DOlm_DIR=olm/build"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 178602da..bae833c3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,6 @@
-cmake_minimum_required(VERSION 3.10)
+# Officially CMake 3.16+ is needed but LGTM.com still sits on eoan that only
+# has CMake 3.13
+cmake_minimum_required(VERSION 3.13)
if (POLICY CMP0092)
cmake_policy(SET CMP0092 NEW)
endif()
@@ -178,7 +180,7 @@ set(ASAPI_DEF_DIR application-service/definitions)
set(ISAPI_DEF_DIR identity/definitions)
if (GTAD_PATH AND MATRIX_DOC_PATH)
- get_filename_component(ABS_GTAD_PATH "${GTAD_PATH}" REALPATH)
+ get_filename_component(ABS_GTAD_PATH "${GTAD_PATH}" PROGRAM PROGRAM_ARGS GTAD_ARGS)
if (EXISTS ${ABS_GTAD_PATH})
get_filename_component(ABS_API_DEF_PATH "${MATRIX_DOC_PATH}/data/api" REALPATH)
if (NOT IS_DIRECTORY ${ABS_API_DEF_PATH})
@@ -200,7 +202,7 @@ if (API_GENERATION_ENABLED)
if (NOT CLANG_FORMAT)
set(CLANG_FORMAT clang-format)
endif()
- get_filename_component(ABS_CLANG_FORMAT "${CLANG_FORMAT}" PROGRAM)
+ get_filename_component(ABS_CLANG_FORMAT "${CLANG_FORMAT}" PROGRAM PROGRAM_ARGS CLANG_FORMAT_ARGS)
if (ABS_CLANG_FORMAT)
set(API_FORMATTING_ENABLED 1)
message( STATUS "clang-format is at ${ABS_CLANG_FORMAT}")
@@ -208,15 +210,14 @@ if (API_GENERATION_ENABLED)
message( STATUS "${CLANG_FORMAT} is NOT FOUND; API files won't be reformatted")
endif ()
- if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.12.0")
- # We use globbing with CONFIGURE_DEPENDS to produce two file lists:
- # one of all API files for clang-format and another of just .cpp
- # files to supply for library source files. Since we expect these
- # file lists to only change due to GTAD invocation, we only use
- # CONFIGURE_DEPENDS when pre-requisites to update API are met.
- # Read comments next to each file(GLOB_RECURSE) for caveats.
- set(add_CONFIGURE_DEPENDS "CONFIGURE_DEPENDS")
- endif()
+ # We use globbing with CONFIGURE_DEPENDS to produce two file lists:
+ # one of all API files for clang-format and another of just .cpp
+ # files to supply for library source files. Since we expect these
+ # file lists to only change due to GTAD invocation, we only use
+ # CONFIGURE_DEPENDS when pre-requisites to update API are met.
+ # Read comments next to each file(GLOB_RECURSE) for caveats.
+ set(add_CONFIGURE_DEPENDS "CONFIGURE_DEPENDS")
+
set(FULL_CSAPI_SRC_DIR ${ABS_API_DEF_PATH}/client-server)
file(GLOB_RECURSE API_DEFS RELATIVE ${PROJECT_SOURCE_DIR}
${FULL_CSAPI_SRC_DIR}/*.yaml
@@ -229,6 +230,7 @@ if (API_GENERATION_ENABLED)
old_sync.yaml- room_initial_sync.yaml- # deprecated
key_backup.yaml- # immature and buggy in terms of API definition
sync.yaml- # we have a better handcrafted implementation
+ ${GTAD_ARGS}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/lib
SOURCES gtad/gtad.yaml
gtad/data.h.mustache
@@ -276,14 +278,16 @@ file(GLOB_RECURSE api_SRCS ${add_CONFIGURE_DEPENDS} ${FULL_CSAPI_DIR}/*.cpp)
add_library(${PROJECT_NAME} ${lib_SRCS} ${api_SRCS})
target_compile_definitions(${PROJECT_NAME} PRIVATE QT_NO_JAVA_STYLE_ITERATORS QT_NO_URL_CAST_FROM_STRING QT_NO_CAST_TO_ASCII)
-if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.16.0"
- AND NOT CMAKE_CXX_COMPILER_ID STREQUAL GNU) # https://bugzilla.redhat.com/show_bug.cgi?id=1721553
- target_precompile_headers(${PROJECT_NAME} PRIVATE lib/converters.h)
-endif ()
+
+target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
+ ${PROJECT_NAME}_VERSION_MINOR=${PROJECT_VERSION_MINOR} ${PROJECT_NAME}_VERSION_PATCH=${PROJECT_VERSION_PATCH}
+ ${PROJECT_NAME}_VERSION_STRING=\"${PROJECT_VERSION}\")
if (${PROJECT_NAME}_ENABLE_E2EE)
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_E2EE_ENABLED)
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES
+ CXX_STANDARD 20
+ CXX_EXTENSIONS OFF
VERSION "${PROJECT_VERSION}"
SOVERSION ${API_VERSION}
INTERFACE_${PROJECT_NAME}_MAJOR_VERSION ${API_VERSION}
@@ -291,8 +295,16 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY
COMPATIBLE_INTERFACE_STRING ${PROJECT_NAME}_MAJOR_VERSION)
+# C++17 required, C++20 desired (see above)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
+# TODO: Bump the CMake requirement and drop the version check here once
+# LGTM upgrades
+if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.16.0"
+ AND NOT CMAKE_CXX_COMPILER_ID STREQUAL GNU) # https://bugzilla.redhat.com/show_bug.cgi?id=1721553
+ target_precompile_headers(${PROJECT_NAME} PRIVATE lib/converters.h)
+endif ()
+
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib>
$<INSTALL_INTERFACE:${${PROJECT_NAME}_INSTALL_INCLUDEDIR}>
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4fdab602..3eeac68c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -155,18 +155,20 @@ just don't bankrupt us with it. Refactoring is welcome.
### Code style and formatting
-As of Quotient 0.6, the C++ standard for newly written code is C++17, with a few
-restrictions, notably:
+As of Quotient 0.6, the C++ standard for newly written code is C++17 with C++20
+compatibility and a few restrictions, notably:
* standard library's _deduction guides_ cannot be used to lighten up syntax
in template instantiation, i.e. you have to still write
- `std::array<int, 2> { 1, 2 }` instead of `std::array { 1, 2 }` or use helpers
- like `std::make_pair` - once we move over to the later Apple toolchain, this
- will be no more necessary.
-* enumerators and slots cannot have `[[attributes]]` because moc of Qt 5.9
- chokes on them. This will be lifted when we move on to Qt 5.12 for the oldest
- supported version.
-* things from `std::filesystem` cannot be used until we push the oldest
- required g++/libc to version 8.
+ `std::array<int, 2> { 1, 2 }` instead of `std::array { 1, 2 }` (or use
+ `Quotient::make_array` helper from `util.h`), use `std::make_pair` to create
+ pairs etc. - once we move over to the later Apple toolchain, this will be
+ no more necessary;
+* enumerators and slots cannot have `[[attributes]]` because moc from Qt 5.12
+ chokes on them - this will be lifted when we move on to Qt 5.13 for the oldest
+ supported version, in the meantime use `Q_DECL_DEPRECATED` and similar Qt
+ macros - they expand to nothing when the code is passed to moc.
+* explicit lists in lambda captures are preferred over `[=]`; note that C++20
+ deprecates implicit `this` capture in `[=]`.
The code style is defined by `.clang-format`, and in general, all C++ files
should follow it. Files with minor deviations from the defined style are still
@@ -408,7 +410,7 @@ I (Kitsune) will be very glad to help you out.
The types map in `gtad.yaml` is the central switchboard when it comes to matching OpenAPI types with C++ (and Qt) ones. It uses the following type attributes aside from pretty obvious "imports:":
* `avoidCopy` - this attribute defines whether a const ref should be used instead of a value. For basic types like int this is obviously unnecessary; but compound types like `QVector` should rather be taken by reference when possible.
* `moveOnly` - some types are not copyable at all and must be moved instead (an obvious example is anything "tainted" with a member of type `std::unique_ptr<>`). The template will use `T&&` instead of `T` or `const T&` to pass such types around.
-* `useOmittable` - wrap types that have no value with "null" semantics (i.e. number types and custom-defined data structures) into a special `Omittable<>` template defined in `converters.h` - a substitute for `std::optional` from C++17 (we're still at C++14 yet).
+* `useOmittable` - wrap types that have no value with "null" semantics (i.e. number types and custom-defined data structures) into a special `Omittable<>` template defined in `converters.h`, a drop-in upgrade over `std::optional`.
* `omittedValue` - an alternative for `useOmittable`, just provide a value used for an omitted parameter. This is used for bool parameters which normally are considered false if omitted (or they have an explicit default value, passed in the "official" GTAD's `defaultValue` variable).
* `initializer` - this is a _partial_ (see GTAD and Mustache documentation for explanations but basically it's a variable that is a Mustache template itself) that specifies how exactly a default value should be passed to the parameter. E.g., the default value for a `QString` parameter is enclosed into `QStringLiteral`.
diff --git a/README.md b/README.md
index 05c629f2..71f2d04c 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,7 @@ The Quotient project aims to produce a Qt5-based SDK to develop applications
for [Matrix](https://matrix.org). libQuotient is a library that enables client
applications. It is the backbone of
[Quaternion](https://github.com/quotient-im/Quaternion),
-[Spectral](https://matrix.org/docs/projects/client/spectral.html) and
-other projects.
+[NeoChat](https://matrix.org/docs/projects/client/neo-chat) and other projects.
Versions 0.5.x and older use the previous name - libQMatrixClient.
## Contacts
@@ -38,61 +37,62 @@ and bundle it with your application.
### Pre-requisites
- A recent Linux, macOS or Windows system (desktop versions are known to work;
mobile operating systems where Qt is available might work too)
- - Recent enough Linux examples: Debian Buster; Fedora 28; openSUSE Leap 15;
- Ubuntu Bionic Beaver.
-- Qt 5 (either Open Source or Commercial), 5.9 or higher;
- 5.12 is recommended, especially if you use qmake
-- A build configuration tool (CMake is recommended, qmake is supported):
- - CMake 3.10 or newer (from your package management system or
- [the official website](https://cmake.org/download/))
- - or qmake (comes with Qt)
-- A C++ toolchain with _reasonably complete_ C++17 support:
- - GCC 7 (Windows, Linux, macOS), Clang 6 (Linux), Apple Clang 10 (macOS)
- and Visual Studio 2017 (Windows) are the oldest officially supported.
-- Any build system that works with CMake and/or qmake should be fine:
- GNU Make, ninja (any platform), NMake, jom (Windows) are known to work.
+ - Recent enough Linux examples: Debian Bullseye; Fedora 33; openSUSE Leap 15.3;
+ Ubuntu Focal Fossa.
+- Qt 5 (either Open Source or Commercial), 5.12 or higher
+- CMake 3.16 or newer (from your package management system or
+ [the official website](https://cmake.org/download/))
+- A C++ toolchain with complete (as much as possible) C++17 and basic C++20:
+ - GCC 10 (Windows, Linux, macOS), Clang 11 (Linux), Apple Clang 12 (macOS)
+ and Visual Studio 2019 (Windows) are the oldest officially supported.
+- Any build system that works with CMake should be fine:
+ GNU Make and ninja on any platform, NMake and jom on Windows are known to work.
+ Ninja is recommended.
#### Linux
-Just install things from the list above using your preferred package manager. If your Qt package base is fine-grained you might want to run cmake/qmake and look at error messages. The library is entirely offscreen (QtCore and QtNetwork are essential) but it also depends on QtGui in order to handle avatar thumbnails.
+Just install things from the list above using your preferred package manager.
+If your Qt package base is fine-grained you might want to run cmake and look
+at error messages. The library is entirely offscreen but aside from QtCore and
+QtNetwork it also depends on QtGui in order to handle avatar thumbnails.
#### macOS
-`brew install qt5` should get you a recent Qt5. If you plan to use CMake, you will need to tell it about the path to Qt by passing `-DCMAKE_PREFIX_PATH=$(brew --prefix qt5)`
+`brew install qt5` should get you a recent Qt5. You may need to pass
+`-DCMAKE_PREFIX_PATH=$(brew --prefix qt5)` to make it aware of the Qt location.
#### Windows
-Install Qt5, using their official installer; if you plan to build with CMake,
-make sure to tick the CMake box in the list of installed components.
-
-The commands in further sections imply that cmake/qmake is in your PATH,
-otherwise you have to prepend those commands with actual paths. As an option
-it's a good idea to run a `qtenv2.bat` script that can be found in
-`C:\Qt\<Qt version>\<toolchain>\bin` (assuming you installed Qt to `C:\Qt`);
-the only thing it does is adding necessary paths to PATH. You might not want
-to run that script on system startup but it's very handy to setup
-the environment before building. For CMake you can alternatively point
-`CMAKE_PREFIX_PATH` to your Qt installation and leave PATH unchanged; but
-in that case you'll have to supply the full path to CMake when calling it.
+Install Qt5 using their official installer; make sure to tick the CMake box
+in the list of installed components unless you already have it installed.
+
+The commands in further sections imply that cmake is in your PATH, otherwise
+you have to prepend those commands with actual paths. It's a good idea to run
+a `qtenv2.bat` script that can be found in `C:\Qt\<Qt version>\<toolchain>\bin`
+(assuming you installed Qt to `C:\Qt`) if you're building from the command line;
+the script adds necessary paths to PATH. You might not want to run that script
+on system startup but it's very handy to setup the environment before building.
+Alternatively you can add the Qt path to `CMAKE_PREFIX_PATH` and leave PATH
+unchanged.
### Using the library
-If you use CMake, `find_package(Quotient)` sets up the client code to use
-libQuotient, assuming the library development files are installed. There's no
-documented procedure to use a preinstalled library with qmake; consider
-introducing a submodule in your source tree and build it along with the rest
-of the application for now. Note also that qmake is considered for phase-out
-in Qt 6 so you should probably think of moving over to CMake eventually.
+If you're just starting a project using libQuotient from scratch, you can copy
+`quotest/CMakeLists.txt` to your project and change `quotest` to your
+project name. If you already have an existing CMakeLists.txt, you need to insert
+a `find_package(Quotient REQUIRED)` line to an appropriate place in it (use
+`find_package(Quotient)` if libQuotient is not a hard dependency for you) and
+then add `Quotient` to your `target_link_libraries()` line.
Building with dynamic linkage is only tested on Linux at the moment and is
a recommended way of linking your application with libQuotient on this platform.
Static linkage is the default on Windows/macOS; feel free to experiment
with dynamic linking and submit PRs if you get reusable results.
-[Quotest](quotest), the test application that comes with libQuotient, includes
-most common use cases such as sending messages, uploading files,
-setting room state etc.; for more extensive usage check out the source code
-of [Quaternion](https://github.com/quotient-im/Quaternion)
-(the reference client of Quotient) or [Spectral](https://gitlab.com/b0/spectral).
-
-To ease the first step, `quotest/CMakeLists.txt` is a good starting point
-for your own CMake-based project using libQuotient.
+As for the actual API usage, a (very basic) overview can be found at
+[the respective wiki page](https://github.com/quotient-im/libQuotient/wiki/libQuotient-overview).
+Beyond that, looking at [Quotest](quotest) - the test application that comes
+with libQuotient - may help you with most common use cases such as sending
+messages, uploading files, setting room state etc. For more extensive usage
+feel free to check out (and copy, with appropriate attribution) the source code
+of [Quaternion](https://github.com/quotient-im/Quaternion) (the reference client
+for libQuotient) or [NeoChat](https://invent.kde.org/network/neochat).
## Building the library
[The source code is at GitHub](https://github.com/quotient-im/libQuotient).
@@ -101,28 +101,29 @@ along with submodules is strongly recommended. If you want to hack on
the library as a part of another project (e.g. you are working on Quaternion
but need to do some changes to the library code), it makes sense
to make a recursive check out of that project (in this case, Quaternion)
-and update the library submodule (also recursively) to its master branch.
+and update the library submodule (also recursively) within the appropriate
+branch. Be mindful of API compatibility restrictions: e.g., Quaternion 0.0.95
+will not build with the master branch of libQuotient.
Tags consisting of digits and periods represent released versions; tags ending
with `-betaN` or `-rcN` mark pre-releases. If/when packaging pre-releases,
it is advised to replace a dash with a tilde.
-### CMake-based
-In the root directory of the project sources:
+The following commands issued in the root directory of the project sources:
```shell script
mkdir build_dir
cd build_dir
cmake .. # [-D<cmake-variable>=<value>...], see below
cmake --build . --target all
```
-This will get you the compiled library in `build_dir` inside your project
-sources. Static builds are tested on all supported platforms, building
-the library as a shared object (aka dynamic library) is supported on Linux
-and macOS but is very likely to be broken on Windows.
-
-The first CMake invocation configures the build. You can pass CMake variables,
-such as `-DCMAKE_PREFIX_PATH="path1;path2;..."` and
-`-DCMAKE_INSTALL_PREFIX=path` here if needed.
+will get you a compiled library in `build_dir` inside your project sources.
+Static builds are tested on all supported platforms, building the library as
+a shared object (aka dynamic library) is supported on Linux and macOS but is
+very likely to be broken on Windows.
+
+The first CMake invocation above configures the build. You can pass CMake
+variables (such as `-DCMAKE_PREFIX_PATH="path1;path2;..."` and
+`-DCMAKE_INSTALL_PREFIX=path`) here if needed.
[CMake documentation](https://cmake.org/cmake/help/latest/index.html)
(pick the CMake version at the top of the page that you use) describes
the standard variables coming with CMake. On top of them, Quotient introduces:
@@ -160,22 +161,6 @@ with the _installed_ library. Installation of the `quotest` binary
along with the rest of the library can be skipped
by setting `Quotient_INSTALL_TESTS` to `OFF`.
-### qmake-based
-The library provides a .pri file with an intention to be included from a bigger project's .pro file. As a starting point you can use `quotest.pro` that will build a minimal example of library usage for you. In the root directory of the project sources:
-```shell script
-qmake quotest.pro
-make all
-```
-This will get you `debug/quotest` and `release/quotest`
-console executables that login to the Matrix server at matrix.org with
-credentials of your choosing (pass the username and password as arguments),
-run a sync long-polling loop and do some tests of the library API. Note that
-qmake didn't really know about C++17 until Qt 5.12 so if your Qt is older
-you may have quite a bit of warnings during the compilation process.
-
-Installing the standalone library with qmake is not implemented yet; PRs are
-welcome though.
-
## Troubleshooting
#### Building fails
diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp
index 239cef28..85066024 100644
--- a/lib/jobs/basejob.cpp
+++ b/lib/jobs/basejob.cpp
@@ -156,8 +156,8 @@ public:
{
// FIXME: use std::array {} when Apple stdlib gets deduction guides for it
static const auto verbs =
- to_array({ QStringLiteral("GET"), QStringLiteral("PUT"),
- QStringLiteral("POST"), QStringLiteral("DELETE") });
+ make_array(QStringLiteral("GET"), QStringLiteral("PUT"),
+ QStringLiteral("POST"), QStringLiteral("DELETE"));
const auto verbWord = verbs.at(size_t(verb));
return verbWord % ' '
% (reply ? reply->url().toString(QUrl::RemoveQuery)
@@ -194,12 +194,12 @@ QUrl BaseJob::requestUrl() const { return d->reply ? d->reply->url() : QUrl(); }
bool BaseJob::isBackground() const { return d->inBackground; }
-const QString& BaseJob::apiEndpoint() const { return d->apiEndpoint; }
+//const QString& BaseJob::apiEndpoint() const { return d->apiUrl.path(); }
-void BaseJob::setApiEndpoint(const QString& apiEndpoint)
-{
- d->apiEndpoint = apiEndpoint;
-}
+//void BaseJob::setApiEndpoint(const QString& apiEndpoint)
+//{
+// d->apiEndpoint = apiEndpoint;
+//}
const BaseJob::headers_t& BaseJob::requestHeaders() const
{
@@ -217,7 +217,7 @@ void BaseJob::setRequestHeaders(const BaseJob::headers_t& headers)
d->requestHeaders = headers;
}
-const QUrlQuery& BaseJob::query() const { return d->requestQuery; }
+QUrlQuery BaseJob::query() const { return d->requestQuery; }
void BaseJob::setRequestQuery(const QUrlQuery& query)
{
@@ -262,14 +262,10 @@ QNetworkReply* BaseJob::reply() { return d->reply.data(); }
QUrl BaseJob::makeRequestUrl(QUrl baseUrl, const QString& path,
const QUrlQuery& query)
{
- auto pathBase = baseUrl.path();
- // QUrl::adjusted(QUrl::StripTrailingSlashes) doesn't help with root '/'
- while (pathBase.endsWith('/'))
- pathBase.chop(1);
- if (!path.startsWith('/')) // Normally API files do start with '/'
- pathBase.push_back('/'); // so this shouldn't be needed these days
-
- baseUrl.setPath(pathBase + path, QUrl::TolerantMode);
+ // Make sure the added path is relative even if it's not (the official
+ // API definitions have the leading slash though it's not really correct).
+ baseUrl = baseUrl.resolved(
+ QUrl(path.mid(path.startsWith('/')), QUrl::TolerantMode));
baseUrl.setQuery(query);
return baseUrl;
}
diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h
index 29443c53..663c121c 100644
--- a/lib/jobs/basejob.h
+++ b/lib/jobs/basejob.h
@@ -325,13 +325,15 @@ Q_SIGNALS:
protected:
using headers_t = QHash<QByteArray, QByteArray>;
+ Q_DECL_DEPRECATED_X("Deprecated due to being unused")
const QString& apiEndpoint() const;
+ Q_DECL_DEPRECATED_X("Deprecated due to being unused")
void setApiEndpoint(const QString& apiEndpoint);
const headers_t& requestHeaders() const;
void setRequestHeader(const headers_t::key_type& headerName,
const headers_t::mapped_type& headerValue);
void setRequestHeaders(const headers_t& headers);
- const QUrlQuery& query() const;
+ QUrlQuery query() const;
void setRequestQuery(const QUrlQuery& query);
const RequestData& requestData() const;
void setRequestData(RequestData&& data);
diff --git a/lib/quotient_common.h b/lib/quotient_common.h
index d225ad63..bb2e6a6b 100644
--- a/lib/quotient_common.h
+++ b/lib/quotient_common.h
@@ -10,22 +10,17 @@
namespace Quotient {
Q_NAMESPACE
-namespace impl {
- template <class T, std::size_t N, std::size_t... I>
- constexpr std::array<std::remove_cv_t<T>, N>
- to_array_impl(T (&&a)[N], std::index_sequence<I...>)
- {
- return { {std::move(a[I])...} };
- }
-}
// std::array {} needs explicit template parameters on macOS because
-// Apple stdlib doesn't have deduction guides for std::array; to alleviate that,
-// to_array() is borrowed from C++20 (thanks to cppreference for the possible
-// implementation: https://en.cppreference.com/w/cpp/container/array/to_array)
-template <typename T, size_t N>
-constexpr auto to_array(T (&& items)[N])
+// Apple stdlib doesn't have deduction guides for std::array. C++20 has
+// to_array() but that can't be borrowed, this time because of MSVC:
+// https://developercommunity.visualstudio.com/t/vc-ice-p1-initc-line-3652-from-stdto-array/1464038
+// Therefore a simpler (but also slightly more wobbly - it resolves the element
+// type using std::common_type<>) make_array facility is implemented here.
+template <typename... Ts>
+constexpr auto make_array(Ts&&... items)
{
- return impl::to_array_impl(std::move(items), std::make_index_sequence<N>{});
+ return std::array<std::common_type_t<Ts...>, sizeof...(items)>(
+ { std::forward<Ts>(items)... });
}
// TODO: code like this should be generated from the CS API definition
@@ -49,9 +44,9 @@ enum class Membership : unsigned int {
Q_DECLARE_FLAGS(MembershipMask, Membership)
Q_FLAG_NS(MembershipMask)
-constexpr inline auto MembershipStrings = to_array(
+constexpr inline auto MembershipStrings = make_array(
// The order MUST be the same as the order in the original enum
- { "join", "leave", "invite", "knock", "ban" });
+ "join", "leave", "invite", "knock", "ban");
//! \brief Local user join-state names
//!
@@ -68,10 +63,10 @@ enum class JoinState : std::underlying_type_t<Membership> {
Q_DECLARE_FLAGS(JoinStates, JoinState)
Q_FLAG_NS(JoinStates)
-constexpr inline auto JoinStateStrings = to_array({
+constexpr inline auto JoinStateStrings = make_array(
MembershipStrings[0], MembershipStrings[1], MembershipStrings[2],
MembershipStrings[3] /* same as MembershipStrings, sans "ban" */
-});
+);
//! \brief Network job running policy flags
//!
diff --git a/lib/room.cpp b/lib/room.cpp
index 72b37f62..bcbb121b 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -1719,6 +1719,12 @@ QString Room::retryMessage(const QString& txnId)
return d->doSendEvent(it->event());
}
+// Lambda defers actual tr() invocation to the moment when translations are
+// initialised
+const auto FileTransferCancelledMsg = [] {
+ return Room::tr("File transfer cancelled");
+};
+
void Room::discardMessage(const QString& txnId)
{
auto it = std::find_if(d->unsyncedEvents.begin(), d->unsyncedEvents.end(),
@@ -1733,7 +1739,7 @@ void Room::discardMessage(const QString& txnId)
if (isJobPending(transferIt->job)) {
transferIt->status = FileTransferInfo::Cancelled;
transferIt->job->abandon();
- emit fileTransferFailed(txnId, tr("File upload cancelled"));
+ emit fileTransferFailed(txnId, FileTransferCancelledMsg());
} else if (transferIt->status == FileTransferInfo::Completed) {
qCWarning(MAIN)
<< "File for transaction" << txnId
@@ -1801,7 +1807,7 @@ QString Room::Private::doPostFile(RoomEventPtr&& msgEvent, const QUrl& localUrl)
"cancelled";
}
});
- connect(q, &Room::fileTransferCancelled, transferJob,
+ connect(q, &Room::fileTransferFailed, transferJob,
[this, txnId](const QString& tId) {
if (tId != txnId)
return;
@@ -2090,16 +2096,16 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename)
void Room::cancelFileTransfer(const QString& id)
{
- const auto it = d->fileTransfers.constFind(id);
- if (it == d->fileTransfers.cend()) {
+ const auto it = d->fileTransfers.find(id);
+ if (it == d->fileTransfers.end()) {
qCWarning(MAIN) << "No information on file transfer" << id << "in room"
<< d->id;
return;
}
if (isJobPending(it->job))
it->job->abandon();
- d->fileTransfers.remove(id);
- emit fileTransferCancelled(id);
+ it->status = FileTransferInfo::Cancelled;
+ emit fileTransferFailed(id, FileTransferCancelledMsg());
}
void Room::Private::dropDuplicateEvents(RoomEvents& events) const
diff --git a/lib/room.h b/lib/room.h
index 9daca076..7290f9a9 100644
--- a/lib/room.h
+++ b/lib/room.h
@@ -715,7 +715,8 @@ Q_SIGNALS:
void fileTransferProgress(QString id, qint64 progress, qint64 total);
void fileTransferCompleted(QString id, QUrl localFile, QUrl mxcUrl);
void fileTransferFailed(QString id, QString errorMessage = {});
- void fileTransferCancelled(QString id);
+ // fileTransferCancelled() is no more here; use fileTransferFailed() and
+ // check the transfer status instead
void callEvent(Quotient::Room* room, const Quotient::RoomEvent* event);
diff --git a/lib/util.cpp b/lib/util.cpp
index 904bfd5a..3de1d169 100644
--- a/lib/util.cpp
+++ b/lib/util.cpp
@@ -119,6 +119,26 @@ QString Quotient::serverPart(const QString& mxId)
return parser.match(mxId).captured(1);
}
+QString Quotient::versionString()
+{
+ return QStringLiteral(Quotient_VERSION_STRING);
+}
+
+int Quotient::majorVersion()
+{
+ return Quotient_VERSION_MAJOR;
+}
+
+int Quotient::minorVersion()
+{
+ return Quotient_VERSION_MINOR;
+}
+
+int Quotient::patchVersion()
+{
+ return Quotient_VERSION_PATCH;
+}
+
// Tests for function_traits<>
using namespace Quotient;
diff --git a/lib/util.h b/lib/util.h
index 78fc9ab7..5bfe6841 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -318,4 +318,9 @@ qreal stringToHueF(const QString& s);
/** Extract the serverpart from MXID */
QString serverPart(const QString& mxId);
+
+QString versionString();
+int majorVersion();
+int minorVersion();
+int patchVersion();
} // namespace Quotient