1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-03-28 14:56:19 +01:00

misc: fix test suite and tooling on Windows

- simplifies and fixes the python build script for Windows
- applies pep8 to the script also

Gitlab: #899
Change-Id: Ieb3debd08ddf1649a46208fc52362d20c504c1b1
This commit is contained in:
Andreas Traczyk 2023-03-27 17:16:23 -04:00
parent 7c30803673
commit df5b955465
9 changed files with 453 additions and 406 deletions

View file

@ -77,6 +77,16 @@ if (${QT_VERSION_MAJOR} STRLESS 6)
endif() endif()
endif() endif()
if(MSVC)
set(DEFAULT_BUILD_TYPE "Debug")
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE
STRING "Choose the type of build." FORCE)
endif()
set(OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/x64/${CMAKE_BUILD_TYPE}")
endif()
# libjamiclient # libjamiclient
add_subdirectory(${LIBCLIENT_SRC_DIR}) add_subdirectory(${LIBCLIENT_SRC_DIR})

View file

@ -55,8 +55,8 @@ Then, you can build the project
### With build.py ### With build.py
The build.py Jami installer uses **python3 (minimum v3.6)**. If it's not installed, The build.py Jami installer uses **python3 (minimum v3.6)**. If it's not installed,
please install it. Then run the following to initialize and update please install it. Then run the following to initialize and update
the submodules to set them at the top of their latest commit (ideal the submodules to set them at the top of their latest commit (ideal
for getting the latest development versions; otherwise, you can use for getting the latest development versions; otherwise, you can use
`git submodule update --init` then checkout specific commits for each `git submodule update --init` then checkout specific commits for each
@ -85,7 +85,7 @@ specify its path using the `--qt` flag, e.g.
`./build.py --install --qt=/home/<username>/Qt/6.2.1/gcc_64`. `./build.py --install --qt=/home/<username>/Qt/6.2.1/gcc_64`.
Now you will have the daemon in `daemon/bin/jamid` and the client in Now you will have the daemon in `daemon/bin/jamid` and the client in
`build/jami`. You can now run Jami using: `build/jami`. You can now run Jami using:
```bash ```bash
./build/jami ./build/jami
@ -163,11 +163,11 @@ Only 64-bit MSVC build can be compiled.
- Download [Visual Studio](https://visualstudio.microsoft.com/) (versions 2019 or 2022). _See the SDK notes below._ - Download [Visual Studio](https://visualstudio.microsoft.com/) (versions 2019 or 2022). _See the SDK notes below._
| | SDK | Toolset | MFC | | | SDK | Toolset | MFC |
| ------------ | ------------ | ---------------------------------------------------- | ---------------- | | ------------ | ------------ | --------------------------------------------------- | ---------------- |
| Requirement: | 10.0.18362.0 | V142 (VisualStudio 2019) / V143 (VisualStudio 2022) | matching Toolset | | Requirement: | 10.0.18362.0 | V142 (VisualStudio 2019) / V143 (VisualStudio 2022) | matching Toolset |
- Install Qt Vs Tools under extensions, and configure msvc2017_64 path under Qt Options. _See the Qt notes below._ - Install Qt Vs Tools under extensions, and configure msvc2017*64 path under Qt Options. \_See the Qt notes below.*
| | Qt Version | | | Qt Version |
| -------------------- | ---------- | | -------------------- | ---------- |
@ -212,8 +212,6 @@ Only 64-bit MSVC build can be compiled.
```bash ```bash
python build.py --install python build.py --install
python extras\scripts\build-windows.py init
python extras\scripts\build-windows.py --qtver <your qt version>
``` ```
- Then you should be able to use the Visual Studio Solution file in client-qt folder **(Configuration = Release, Platform = x64)** - Then you should be able to use the Visual Studio Solution file in client-qt folder **(Configuration = Release, Platform = x64)**
@ -243,11 +241,11 @@ Only 64-bit MSVC build can be compiled.
**Jami** **Jami**
- Make sure that daemon is built first. Then, - Make sure that daemon is built first. Then,
```bash ```
python extras\scripts\build-windows.py init python extras\scripts\build-windows.py --init
python extras\scripts\build-windows.py python extras\scripts\build-windows.py --qtver <your qt version>
``` ```
Note: if your qt version is different than 6.2.3, you need to use `python extras\scripts\build-windows.py --qtver <your qt version>`. Note: if your qt version is different than 6.2.3, you need to use `python extras\scripts\build-windows.py --qtver <your qt version>`.
@ -298,7 +296,7 @@ Built client could be find in `build/Jami`
- We currently use [GoogleTest](https://github.com/google/googletest) and [Qt Quick Test](https://doc.qt.io/qt-5/qtquicktest-index.html#introduction) in our product. To build and run tests, you could use the following command. - We currently use [GoogleTest](https://github.com/google/googletest) and [Qt Quick Test](https://doc.qt.io/qt-5/qtquicktest-index.html#introduction) in our product. To build and run tests, you could use the following command.
``` ```
python extras\scripts\build-windows.py [runtests, pack] python extras\scripts\build-windows.py --tests
``` ```
- Note that, for tests, the path of local storage files for jami will be changed based on following environment variables. - Note that, for tests, the path of local storage files for jami will be changed based on following environment variables.

View file

@ -12,25 +12,25 @@ Jami provides all its users a universal communication tool, autonomous, free, se
For more information about the jami project, see the following: For more information about the jami project, see the following:
+ Main website: https://jami.net/ - Main website: https://jami.net/
+ Download: https://jami.net/download/ - Download: https://jami.net/download/
+ Bug tracker: https://git.jami.net/ - Bug tracker: https://git.jami.net/
+ Repositories: https://review.jami.net - Repositories: https://review.jami.net
# Getting involved # Getting involved
+ Browse our [current issues](https://git.jami.net/savoirfairelinux/jami-client-qt/issues), or file an issue. - Browse our [current issues](https://git.jami.net/savoirfairelinux/jami-client-qt/issues), or file an issue.
+ IRC: #jami on libera.chat - IRC: #jami on libera.chat
+ ML: jami@gnu.org - ML: jami@gnu.org
+ Documentation: https://docs.jami.net - Documentation: https://docs.jami.net
+ Localization happens on [Transifex](https://www.transifex.com/savoirfairelinux/jami/dashboard/) - Localization happens on [Transifex](https://www.transifex.com/savoirfairelinux/jami/dashboard/)
+ [Our contributions propositions](https://git.jami.net/groups/savoirfairelinux/-/epics/1) or [feature requests](https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/4.3.-Features-requests) asked by the community - [Our contributions propositions](https://git.jami.net/groups/savoirfairelinux/-/epics/1) or [feature requests](https://docs.jami.net/developer/feature-requests.html) asked by the community
+ Packaging: Feel free to contact us - Packaging: Feel free to contact us
## Notes ## Notes
+ Coding style is managed by the clang-format, if you want to contribute, please use the pre-commit hook automatically installed with `./make-ring.py --init` - Coding style is managed by the clang-format, if you want to contribute, please use the pre-commit hook automatically installed with `./build.py --init`
+ We use gerrit for our review. Please read https://git.jami.net/savoirfairelinux/ring-project/wikis/tutorials/Working-with-gerrit if you want to submit patches. - We use gerrit for our review. Please read about [working with Gerrit](https://docs.jami.net/developer/working-with-gerrit.html) if you want to submit patches.
## Build ## Build

View file

@ -347,7 +347,7 @@ def run_install(args):
['python winmake.py -iv ' ['python winmake.py -iv '
f'-s {args.sdk} -b daemon']) f'-s {args.sdk} -b daemon'])
build_windows = 'extras/scripts/build-windows.py' build_windows = 'extras/scripts/build-windows.py'
execute_script([f'python {build_windows} init']) execute_script([f'python {build_windows} --init'])
execute_script([f'python {build_windows}']) execute_script([f'python {build_windows}'])
return True return True

View file

@ -118,9 +118,8 @@ pipeline {
""") """)
// Run tests // Run tests
exec_cmd(""" exec_cmd("""
cd ${dockerTopDir} cd ${dockerTopDir}/build/tests
cd tests/qml/src HOME=/tmp ctest -j${cpuCount} -V -C Release
HOME=/tmp ../../../build/tests/qml_tests
""") """)
} }
} }

View file

@ -1,177 +1,214 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""
Build, test, and package the project.
This script provides methods to build the project, build and run qml tests,
and package the project for Windows.
usage: build.py [-h] [-a ARCH] [-c CONFIG] [-t] [-i] [-v] {pack} ...
optional arguments:
-a ARCH, --arch ARCH Sets the build architecture
-c CONFIG, --config CONFIG
Sets the build configuration type
-t, --tests Build and run tests
-i, --init Initialize submodules
-v, --version Show the version number and exit
-s, --skip-build Only do packaging or run tests, skip building
positional arguments:
{pack}
usage: build.py pack [-h] [-s] (-m | -z)
mutually exclusive required arguments:
-m, --msi Build MSI installer
-z, --zip Build ZIP archive
examples:
1. build.py --init pack --msi # Build the application from scratch and
an MSI installer
2. build.py --init --tests # Build the application from scratch and
build and run tests
build.py pack --zip # Generate a ZIP archive of the application
without building
"""
import tempfile
import re
import sys import sys
import os import os
import subprocess import subprocess
import platform import platform
import argparse import argparse
import multiprocessing import multiprocessing
import fileinput
from enum import Enum
qt_version_default = '6.2.3'
vs_where_path = os.path.join( # Qt information
os.environ['ProgramFiles(x86)'], 'Microsoft Visual Studio', 'Installer', 'vswhere.exe' QT_VERSION = "6.2.3"
) if sys.platform == "win32":
QT_PATH = os.path.join("c:", os.sep, "Qt")
QT_KIT_PATH = "msvc2019_64"
else:
QT_PATH = ""
QT_KIT_PATH = "clang_64"
qt_root_path = os.getenv("QT_ROOT_DIRECTORY", QT_PATH)
host_is_64bit = (False, True)[platform.machine().endswith('64')] # Visual Studio helpers
VS_WHERE_PATH = ""
if sys.platform == "win32":
VS_WHERE_PATH = os.path.join(
os.environ["ProgramFiles(x86)"],
"Microsoft Visual Studio",
"Installer",
"vswhere.exe",
)
WIN_SDK_VERSION = "10.0.18362.0"
# Build/project environment information
is_jenkins = "JENKINS_URL" in os.environ
host_is_64bit = (False, True)[platform.machine().endswith("64")]
this_dir = os.path.dirname(os.path.realpath(__file__)) this_dir = os.path.dirname(os.path.realpath(__file__))
repo_root_dir = os.path.dirname(os.path.dirname(this_dir)) # the repo root is two levels up from this script
build_dir = os.path.join(repo_root_dir, 'build') repo_root_dir = os.path.abspath(os.path.join(this_dir, os.pardir, os.pardir))
build_dir = os.path.join(repo_root_dir, "build")
temp_path = os.environ['TEMP']
openssl_include_dir = 'C:\\Qt\\Tools\\OpenSSL\\Win_x64\\include\\openssl'
qt_path = os.path.join('c:', os.sep, 'Qt')
qt_kit_path = 'msvc2019_64'
qt_root_path = os.getenv('QT_ROOT_DIRECTORY', qt_path)
# project path
installer_project = os.path.join(
repo_root_dir, 'JamiInstaller', 'JamiInstaller.wixproj')
unit_test_project = os.path.join(build_dir, 'tests', 'unittests.vcxproj')
qml_test_project = os.path.join(build_dir, 'tests', 'qml_tests.vcxproj')
# test executable command
qml_test_exe = os.path.join(repo_root_dir, 'x64', 'test', 'qml_tests.exe -input ') + \
os.path.join(repo_root_dir, 'tests', 'qml')
unit_test_exe = os.path.join(repo_root_dir, 'x64', 'test', 'unittests.exe')
def execute_cmd(cmd, with_shell=False, env_vars=None, cmd_dir=repo_root_dir): def execute_cmd(cmd, with_shell=False, env_vars=None, cmd_dir=repo_root_dir):
p = subprocess.Popen(cmd, """Execute a command with subprocess."""
shell=with_shell, proc = subprocess.Popen(
stdout=sys.stdout, cmd,
env=env_vars, shell=with_shell,
cwd=cmd_dir) stdout=sys.stdout,
_, _ = p.communicate() stderr=sys.stderr,
return p.returncode env=env_vars,
cwd=cmd_dir,
)
_, _ = proc.communicate()
return proc.returncode
def getLatestVSVersion(): def get_latest_vs_version():
"""Find the latest visual c++ compiler tools version."""
args = [ args = [
'-latest', "-latest",
'-products *', "-products *",
'-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64', "-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
'-property installationVersion' "-property installationVersion",
] ]
cmd = [vs_where_path] + args cmd = [VS_WHERE_PATH] + args
output = subprocess.check_output(' '.join(cmd)).decode('utf-8') output = subprocess.check_output(" ".join(cmd)).decode("utf-8")
if output: if output:
return output.splitlines()[0].split('.')[0] return output.splitlines()[0].split(".")[0]
else: else:
return return
def findVSLatestDir(): def find_latest_vs_dir():
"""Find the latest visual c++ compiler tools path."""
args = [ args = [
'-latest', "-latest",
'-products *', "-products *",
'-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64', "-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
'-property installationPath' "-property installationPath",
] ]
cmd = [vs_where_path] + args cmd = [VS_WHERE_PATH] + args
output = subprocess.check_output( output = subprocess.check_output(
' '.join(cmd)).decode('utf-8', errors='ignore') " ".join(cmd)).decode("utf-8", errors="ignore")
if output: if output:
return output.splitlines()[0] return output.splitlines()[0]
else: else:
return return
def findMSBuild(): def find_ms_build():
filename = 'MSBuild.exe' """Find the latest msbuild executable."""
for root, _, files in os.walk(findVSLatestDir() + r'\\MSBuild'): filename = "MSBuild.exe"
vs_path = find_latest_vs_dir()
if vs_path is None:
return
for root, _, files in os.walk(os.path.join(vs_path, "MSBuild")):
if filename in files: if filename in files:
return os.path.join(root, filename) return os.path.join(root, filename)
def getMSBuildArgs(arch, config_str, configuration_type=''): msbuild_cmd = find_ms_build()
def get_ms_build_args(arch, config_str, toolset=""):
"""Get an array of msbuild command args."""
msbuild_args = [ msbuild_args = [
'/nologo', "/nologo",
'/verbosity:minimal', "/verbosity:minimal",
'/maxcpucount:' + str(multiprocessing.cpu_count()), "/maxcpucount:" + str(multiprocessing.cpu_count()),
'/p:Platform=' + arch, "/p:Platform=" + arch,
'/p:Configuration=' + config_str, "/p:Configuration=" + config_str,
'/p:useenv=true'] "/p:useenv=true",
if (configuration_type != ''): ]
msbuild_args.append('/p:ConfigurationType=' + configuration_type) if toolset != "":
msbuild_args.append("/p:PlatformToolset=" + toolset)
return msbuild_args return msbuild_args
def getVSEnv(arch='x64', platform='', version=''): def get_vs_env(arch="x64", _platform="", version=""):
env_cmd = 'set path=%path:"=% && ' + \ """Get the vcvarsall.bat command."""
getVSEnvCmd(arch, platform, version) + ' && set' vs_env_cmd = get_vs_env_cmd(arch, _platform, version)
p = subprocess.Popen(env_cmd, if vs_env_cmd is None:
shell=True, return {}
stdout=subprocess.PIPE) env_cmd = f'set path=%path:"=% && {vs_env_cmd} && set'
stdout, _ = p.communicate() proc = subprocess.Popen(env_cmd, shell=True, stdout=subprocess.PIPE)
out = stdout.decode('utf-8', errors='ignore').split("\r\n")[5:-1] stdout, _ = proc.communicate()
return dict(s.split('=', 1) for s in out) out = stdout.decode("utf-8", errors="ignore").split("\r\n")[5: -1]
return dict(s.split("=", 1) for s in out)
def getVSEnvCmd(arch='x64', platform='', version=''): def get_vs_env_cmd(arch="x64", _platform="", version=""):
vcEnvInit = [findVSLatestDir() + r'\VC\Auxiliary\Build\"vcvarsall.bat'] """Get the vcvarsall.bat command."""
if platform != '': vs_path = find_latest_vs_dir()
args = [arch, platform, version] if vs_path is None:
return
vc_env_init = [os.path.join(
vs_path, "VC", "Auxiliary", "Build") + r'\"vcvarsall.bat']
if _platform != "":
args = [arch, _platform, version]
else: else:
args = [arch, version] args = [arch, version]
if args: if args:
vcEnvInit.extend(args) vc_env_init.extend(args)
vcEnvInit = 'call \"' + ' '.join(vcEnvInit) vc_env_init = 'call "' + " ".join(vc_env_init)
return vcEnvInit return vc_env_init
def replace_necessary_vs_prop(project_path, toolset, sdk_version): def build_project(msbuild_args, proj, env_vars):
# force toolset """
replace_vs_prop(project_path, Use msbuild to build a project.
'PlatformToolset',
toolset)
# force unicode
replace_vs_prop(project_path,
'CharacterSet',
'Unicode')
# force sdk_version
replace_vs_prop(project_path,
'WindowsTargetPlatformVersion',
sdk_version)
Used specifically to build installer project and deps.
def build_project(msbuild, msbuild_args, proj, env_vars): """
args = [] args = []
args.extend(msbuild_args) args.extend(msbuild_args)
args.append(proj) args.append(proj)
cmd = [msbuild] cmd = [msbuild_cmd]
cmd.extend(args) cmd.extend(args)
if (execute_cmd(cmd, True, env_vars)): if execute_cmd(cmd, True, env_vars):
print("Build failed when building ", proj) print("Failed when building ", proj)
sys.exit(1) sys.exit(1)
def replace_vs_prop(filename, prop, val):
p = re.compile(r'(?s)<' + prop + r'\s?.*?>(.*?)<\/' + prop + r'>')
val = r'<' + prop + r'>' + val + r'</' + prop + r'>'
with fileinput.FileInput(filename, inplace=True) as file:
for line in file:
print(re.sub(p, val, line), end='')
def init_submodules(): def init_submodules():
print('Initializing submodules...') """Initialize any git submodules in the project."""
print("Initializing submodules...")
if (execute_cmd(['git', 'submodule', 'update', '--init'], False)): if execute_cmd(["git", "submodule", "update", "--init"], False):
print('Submodule initialization error.') print("Submodule initialization error.")
else: else:
if (execute_cmd(['git', 'submodule', 'update', '--recursive'], False)): if execute_cmd(["git", "submodule", "update", "--recursive"], False):
print('Submodule recursive checkout error.') print("Submodule recursive checkout error.")
else: else:
print('Submodule recursive checkout finished.') print("Submodule recursive checkout finished.")
def build_deps(): def build_deps():
"""Build the dependencies for the project."""
print('Patching and building qrencode') print('Patching and building qrencode')
apply_cmd = [ apply_cmd = [
'git', 'git',
@ -183,163 +220,249 @@ def build_deps():
qrencode_dir = os.path.join(repo_root_dir, '3rdparty', 'qrencode-win32') qrencode_dir = os.path.join(repo_root_dir, '3rdparty', 'qrencode-win32')
patch_file = os.path.join(repo_root_dir, 'qrencode-win32.patch') patch_file = os.path.join(repo_root_dir, 'qrencode-win32.patch')
apply_cmd.append(patch_file) apply_cmd.append(patch_file)
print(apply_cmd) if execute_cmd(apply_cmd, False, None, qrencode_dir):
if (execute_cmd(apply_cmd, False, None, qrencode_dir)):
print("Couldn't patch qrencode-win32.") print("Couldn't patch qrencode-win32.")
vs_env_vars = {} vs_env_vars = {}
vs_env_vars.update(getVSEnv()) vs_env_vars.update(get_vs_env())
msbuild = findMSBuild() msbuild_args = get_ms_build_args("x64", "Release-Lib")
if not os.path.isfile(msbuild):
raise IOError('msbuild.exe not found. path=' + msbuild)
msbuild_args = getMSBuildArgs('x64', 'Release-Lib')
proj_path = os.path.join(qrencode_dir, 'qrencode-win32',
'vc8', 'qrcodelib', 'qrcodelib.vcxproj')
build_project(msbuild, msbuild_args, proj_path, vs_env_vars) proj_path = os.path.join(
qrencode_dir, "qrencode-win32", "vc8", "qrcodelib", "qrcodelib.vcxproj"
)
build_project(msbuild_args, proj_path, vs_env_vars)
def build(config_str, qtver, tests=False): def cmake_generate(options, env_vars, cmake_build_dir):
"""Generate the cmake project."""
print("Generating cmake project...")
# Pretty-print the options
print("Options:")
for option in options:
print(" " + option)
cmake_cmd = ["cmake", ".."]
cmake_cmd.extend(options)
if execute_cmd(cmake_cmd, False, env_vars, cmake_build_dir):
print("CMake generation error.")
return False
return True
def cmake_build(config_str, env_vars, cmake_build_dir):
"""Use cmake to build the project."""
print("Building cmake project...")
cmake_cmd = ["cmake", "--build", ".", "--config", config_str, "--", "-m"]
if execute_cmd(cmake_cmd, False, env_vars, cmake_build_dir):
print("CMake build error.")
return False
return True
def build(config_str, qtver, tests):
"""Use cmake to build the project."""
print("Building with Qt " + qtver) print("Building with Qt " + qtver)
vs_env_vars = {} vs_env_vars = {}
vs_env_vars.update(getVSEnv()) vs_env_vars.update(get_vs_env())
qt_dir = os.path.join(qt_root_path, qtver, qt_kit_path) # Get the Qt bin directory.
daemon_dir = os.path.join(repo_root_dir, 'daemon') qt_dir = os.path.join(qt_root_path, qtver, QT_KIT_PATH)
daemon_bin_dir = os.path.join(daemon_dir, 'build/x64/ReleaseLib_win32/bin')
# Get the daemon bin/include directories.
daemon_dir = os.path.join(repo_root_dir, "daemon")
daemon_bin_dir = os.path.join(
daemon_dir, "build", "x64", "ReleaseLib_win32", "bin")
# We need to update the minimum SDK version to be able to # We need to update the minimum SDK version to be able to
# build with system theme support # build with system theme support
cmake_options = [ cmake_options = [
'-DWITH_DAEMON_SUBMODULE=ON', "-DWITH_DAEMON_SUBMODULE=ON",
'-DCMAKE_PREFIX_PATH=' + qt_dir, "-DCMAKE_PREFIX_PATH=" + qt_dir,
'-DCMAKE_INSTALL_PREFIX=' + daemon_bin_dir, "-DCMAKE_INSTALL_PREFIX=" + daemon_bin_dir,
'-DLIBJAMI_INCLUDE_DIR=' + daemon_dir + '\\src\\jami', "-DLIBJAMI_INCLUDE_DIR=" + daemon_dir + "\\src\\jami",
'-DCMAKE_SYSTEM_VERSION=10.0.18362.0' "-DCMAKE_SYSTEM_VERSION=" + WIN_SDK_VERSION,
"-DCMAKE_BUILD_TYPE=" + config_str,
"-DENABLE_TESTS=" + str(tests).lower(),
"-DBETA=" + str((0, 1)[config_str == "Beta"]),
] ]
if tests:
cmake_options.append('-DENABLE_TESTS=true')
# Make sure the build directory exists.
if not os.path.exists(build_dir): if not os.path.exists(build_dir):
os.makedirs(build_dir) os.makedirs(build_dir)
cmd = ['cmake', '..'] if not cmake_generate(cmake_options, vs_env_vars, build_dir):
if (config_str == 'Beta'):
cmake_options.append('-DBETA=1')
print('Generating…')
cmd.extend(cmake_options)
if (execute_cmd(cmd, False, vs_env_vars, build_dir)):
print("Cmake generate error") print("Cmake generate error")
sys.exit(1) sys.exit(1)
print('Building…') if not cmake_build(config_str, vs_env_vars, build_dir):
cmd = [
'cmake', '--build', '.',
'--config', 'Release',
'--', '-m'
]
if (execute_cmd(cmd, False, vs_env_vars, build_dir)):
print("Cmake build error") print("Cmake build error")
sys.exit(1) sys.exit(1)
def run_tests(mute_jamid, output_to_files): def run_tests(config_str):
print('Running client tests') """Run tests."""
print("Running client tests")
test_exe_command_list = [qml_test_exe, unit_test_exe] qt_dir = os.path.join(qt_root_path, QT_VERSION, QT_KIT_PATH)
os.environ["PATH"] += os.pathsep + os.path.join(qt_dir, 'bin')
os.environ["QT_QPA_PLATFORM"] = "offscreen"
os.environ["QT_QUICK_BACKEND"] = "software"
os.environ['QT_QPA_FONTDIR'] = os.path.join(
repo_root_dir, 'resources', 'fonts')
os.environ['QT_PLUGIN_PATH'] = os.path.join(qt_dir, 'plugins')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join(
qt_dir, 'plugins', 'platforms')
if mute_jamid: tests_dir = os.path.join(build_dir, "tests")
test_exe_command_list[0] += ' -mutejamid' if execute_cmd(["ctest", "-V", "-C", config_str],
test_exe_command_list[1] += ' -mutejamid' False, None, tests_dir):
if output_to_files: print("Tests failed.")
test_exe_command_list[0] += ' -o ' + \ sys.exit(1)
os.path.join(repo_root_dir, 'x64', 'test', 'qml_tests.txt')
test_exe_command_list[1] += ' > ' + \
os.path.join(repo_root_dir, 'x64', 'test', 'unittests.txt')
test_result_code = 0
# make sure that the tests are rendered offscreen
os.environ["QT_QPA_PLATFORM"] = 'offscreen'
os.environ["QT_QUICK_BACKEND"] = 'software'
for test_exe_command in test_exe_command_list:
if (execute_cmd(test_exe_command, True)):
test_result_code = 1
sys.exit(test_result_code)
def generate_msi_installer(config_str): def generate_msi(version):
print('Generating application installer...') """Package MSI for Windows."""
print("Generating MSI installer...")
vs_env_vars = {} vs_env_vars = {}
vs_env_vars.update(getVSEnv()) vs_env_vars.update(get_vs_env())
msbuild = findMSBuild() msbuild_args = get_ms_build_args("x64", "Release")
if not os.path.isfile(msbuild): installer_dir = os.path.join(repo_root_dir, "JamiInstaller")
raise IOError('msbuild.exe not found. path=' + msbuild) installer_project = os.path.join(installer_dir, "JamiInstaller.wixproj")
build_project(msbuild_args, installer_project, vs_env_vars)
msi_dir = os.path.join(installer_dir, "bin", "Release", "en-us")
msi_file_file = os.path.join(
msi_dir, "jami.release.x64.msi")
msi_version_file = os.path.join(
msi_dir, "jami-" + version + ".msi")
try:
os.rename(msi_file_file, msi_version_file)
except FileExistsError:
os.remove(msi_version_file)
os.rename(msi_file_file, msi_version_file)
msbuild_args = getMSBuildArgs('x64', config_str)
build_project(msbuild, msbuild_args, installer_project, vs_env_vars) def generate_zip(version):
"""Package archive for Windows."""
print('Generating 7z archive...')
# Generate 7z archive for Windows
app_output_dir = os.path.join(repo_root_dir, 'x64', 'Release')
app_files = os.path.join(app_output_dir, '*')
# TODO: exclude Jami.PDB, .deploy.stamp, and vc_redist.x64.exe
artifacts_dir = os.path.join(build_dir, 'artifacts')
if not os.path.exists(artifacts_dir):
os.makedirs(artifacts_dir)
zip_file = os.path.join(artifacts_dir, 'jami-' +
version + '.7z')
cmd = ['7z', 'a', '-t7z', '-r', zip_file, app_files]
if execute_cmd(cmd, False):
print('Generating 7z error.')
def get_version():
"""Get version from git tag."""
version = ""
cmd = ["git", "describe", "--tags", "--abbrev=0"]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
out, _ = proc.communicate()
if out:
version = out.decode("utf-8").strip()
# transform slashes to dashes (if any)
version = version.replace("/", "-")
return version
def parse_args(): def parse_args():
ap = argparse.ArgumentParser(description="Client qt build tool") """Parse arguments."""
subparser = ap.add_subparsers(dest="subparser_name") parser = argparse.ArgumentParser(description="Client build tool")
subparsers = parser.add_subparsers(dest="subcommand")
ap.add_argument( parser.add_argument(
'-t', '--runtests', action='store_true', "-a", "--arch", default="x64", help="Sets the build architecture")
help='Build and run tests') parser.add_argument(
ap.add_argument( "-t", "--tests", action="store_true", help="Build and run tests")
parser.add_argument(
'-v', '--version', action='store_true',
help='Retrieve the current version')
parser.add_argument(
'-b', '--beta', action='store_true', '-b', '--beta', action='store_true',
help='Build Qt Client in Beta Config') help='Build Qt Client in Beta Config')
ap.add_argument( parser.add_argument(
'-q', '--qtver', default=qt_version_default, '-q', '--qtver', default=QT_VERSION,
help='Sets the version of Qmake') help='Sets the version of Qmake')
ap.add_argument( parser.add_argument(
'-m', '--mute', action='store_true', default=False, "-i", "--init", action="store_true", help="Initialize submodules")
help='Mute jamid logs') parser.add_argument(
"-s",
"--skip-build",
action="store_true",
default=False,
help="Only do packaging or run tests, skip build step")
subparser.add_parser('init') pack_arg_parser = subparsers.add_parser("pack")
subparser.add_parser('pack') pack_group = pack_arg_parser.add_mutually_exclusive_group(required=True)
pack_group.add_argument(
"-m", "--msi", action="store_true", help="Build MSI installer")
pack_group.add_argument(
"-z", "--zip", action="store_true", help="Build ZIP archive")
run_test = subparser.add_parser('runtests') return parser.parse_args()
run_test.add_argument(
'-l', '--logtests', action='store_true', default=False,
help='Output tests log to files')
parsed_args = ap.parse_args()
return parsed_args
def main(): def main():
"""Parse options and run the appropriate command."""
if not host_is_64bit: if not host_is_64bit:
print('These scripts will only run on a 64-bit Windows system for now!') print("These scripts will only run on a 64-bit system for now.")
sys.exit(1) sys.exit(1)
if sys.platform == "win32":
vs_version = get_latest_vs_version()
if vs_version is None or int(vs_version) < 15:
print("Visual Studio 2017 or later is required.")
sys.exit(1)
if int(getLatestVSVersion()) < 16: # Quit if msbuild was not found
print('These scripts require at least Visual Studio v16 2019!') if msbuild_cmd is None:
print("msbuild.exe not found")
sys.exit(1) sys.exit(1)
parsed_args = parse_args() parsed_args = parse_args()
if parsed_args.subparser_name == 'init': if parsed_args.version:
print(get_version())
sys.exit(0)
if parsed_args.init:
init_submodules() init_submodules()
build_deps() build_deps()
elif parsed_args.subparser_name == 'pack': sys.exit(0)
config = ('Release', 'Beta')[parsed_args.beta]
generate_msi_installer(config) config_str = ('Release', 'Beta')[parsed_args.beta]
sys.exit(1) skip_build = parsed_args.skip_build
if parsed_args.subcommand == "pack":
if not skip_build:
build(config_str, parsed_args.qtver, False)
elif parsed_args.msi:
generate_msi(get_version())
elif parsed_args.zip:
generate_zip(get_version())
else: else:
config = ('Release', 'Beta')[parsed_args.beta] if not skip_build:
build(config, parsed_args.qtver, parsed_args.runtests) build(config_str, parsed_args.qtver,
if parsed_args.runtests: parsed_args.tests)
run_tests(parsed_args.mutejamid, parsed_args.outputtofiles) if parsed_args.tests:
run_tests(config_str)
print("Done")
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View file

@ -1,4 +1,18 @@
find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED QuickTest Test Widgets) cmake_minimum_required(VERSION 3.16)
enable_testing(true)
set(QT_TESTING_MODULES
${QT_MODULES}
QuickControls2
QuickTest
Test
Widgets
)
find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED ${QT_TESTING_MODULES})
foreach(MODULE ${QT_TESTING_MODULES})
list(APPEND QT_TEST_LIBS "Qt::${MODULE}")
endforeach()
if(MSVC) if(MSVC)
# Download and unpack googletest for windows # Download and unpack googletest for windows
@ -14,8 +28,7 @@ else()
find_package(GTest REQUIRED) find_package(GTest REQUIRED)
endif() endif()
enable_testing(true) set(QML_TEST_LIBS ${QT_LIBS} ${LIBCLIENT_NAME} ${QT_TEST_LIBS})
set(QML_TEST_LIBS ${QT_LIBS} ${LIBCLIENT_NAME} Qt::QuickTest Qt::Test Qt::Widgets)
set(TESTS_INCLUDES set(TESTS_INCLUDES
${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/tests/qml ${CMAKE_SOURCE_DIR}/tests/qml
@ -29,168 +42,80 @@ target_compile_definitions(test_common_obj PRIVATE ENABLE_TESTS="ON")
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src)
# QML tests set(COMMON_TESTS_SOURCES
add_executable(qml_tests ${QML_RESOURCES}
${CMAKE_SOURCE_DIR}/tests/qml/main.cpp ${QML_RESOURCES_QML}
${QML_RESOURCES} $<TARGET_OBJECTS:test_common_obj>)
${QML_RESOURCES_QML}
${TEST_QML_RESOURCES}
${SFPM_OBJECTS}
$<TARGET_OBJECTS:test_common_obj>)
target_link_libraries(qml_tests if(MSVC)
${QML_TEST_LIBS} list(APPEND WINDOWS_LIBS
${test_common_objects}) ${QTWRAPPER_LIB}
${RINGCLIENT_STATIC_LIB}
${QRENCODE_LIB}
${GNUTLS_LIB}
${DRING_LIB}
${WINDOWS_SYS_LIBS})
target_compile_definitions(qml_tests PRIVATE ENABLE_TESTS="ON") list(APPEND WINDOWS_INCLUDES
${LRC_SRC_PATH}
${DRING_SRC_PATH})
else()
set(PTHREAD_LIB pthread)
endif()
# Unittests string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
set(UNIT_TESTS_HEADER_FILES ${CMAKE_SOURCE_DIR}/tests/unittests/globaltestenvironment.h)
set(QML_TESTS_SOURCE_FILES
${CMAKE_SOURCE_DIR}/tests/qml/main.cpp
${TEST_QML_RESOURCES}
${SFPM_OBJECTS}
${COMMON_TESTS_SOURCES})
set(UNIT_TESTS_SOURCE_FILES set(UNIT_TESTS_SOURCE_FILES
${CMAKE_SOURCE_DIR}/tests/unittests/main_unittest.cpp ${CMAKE_SOURCE_DIR}/tests/unittests/main_unittest.cpp
${CMAKE_SOURCE_DIR}/tests/unittests/account_unittest.cpp ${CMAKE_SOURCE_DIR}/tests/unittests/account_unittest.cpp
${CMAKE_SOURCE_DIR}/tests/unittests/contact_unittest.cpp) ${CMAKE_SOURCE_DIR}/tests/unittests/contact_unittest.cpp
${CMAKE_SOURCE_DIR}/tests/unittests/globaltestenvironment.h
${COMMON_TESTS_SOURCES})
add_executable(unittests set(ALL_TESTS_LIBS
${UNIT_TESTS_HEADER_FILES} ${QML_TEST_LIBS}
${UNIT_TESTS_SOURCE_FILES} gtest
${QML_RESOURCES} ${ringclient}
${QML_RESOURCES_QML} ${qrencode}
$<TARGET_OBJECTS:test_common_obj>) ${X11}
${LIBNM_LIBRARIES}
${LIBNOTIFY_LIBRARIES}
${LIBGDKPIXBUF_LIBRARIES}
${WINDOWS_LIBS})
target_link_libraries(unittests set(ALL_TESTS_INCLUDES
${QML_TEST_LIBS} ${TESTS_INCLUDES}
${test_common_objects} ${LRC}/include/libringclient
gtest) ${LRC}/include
${LIBNM_INCLUDE_DIRS}
${LIBNOTIFY_INCLUDE_DIRS}
${LIBGDKPIXBUF_INCLUDE_DIRS}
${WINDOWS_INCLUDES})
target_compile_definitions(unittests PRIVATE ENABLE_TESTS="ON") function(setup_test TEST_NAME TEST_SOURCES TEST_INPUT)
string(TOLOWER ${TEST_NAME} TEST_BINARY_NAME)
add_executable(${TEST_BINARY_NAME} ${TEST_SOURCES})
target_compile_definitions(${TEST_BINARY_NAME} PRIVATE ENABLE_TESTS="ON")
target_link_libraries(${TEST_BINARY_NAME} ${ALL_TESTS_LIBS})
target_include_directories(${TEST_BINARY_NAME} PUBLIC ${ALL_TESTS_INCLUDES})
if(MSVC)
set_target_properties(${TEST_BINARY_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_${BUILD_TYPE} ${OUTPUT_DIRECTORY})
endif()
add_test(NAME ${TEST_NAME} COMMAND ${TEST_BINARY_NAME} -input ${TEST_INPUT} -mutejamid)
endfunction()
if(MSVC) # QML tests
include_directories(${LRC_SRC_PATH} setup_test(Qml_Tests
${DRING_SRC_PATH}) "${QML_TESTS_SOURCE_FILES}"
"${PROJECT_SOURCE_DIR}/tests/qml/src")
# QML tests # Unit tests
target_link_libraries(qml_tests setup_test(Unit_Tests
${QTWRAPPER_LIB} "${UNIT_TESTS_SOURCE_FILES}" "")
${RINGCLIENT_STATIC_LIB}
${QRENCODE_LIB}
${GNUTLS_LIB}
${DRING_LIB}
${WINDOWS_SYS_LIBS})
target_include_directories(qml_tests PUBLIC
${TESTS_INCLUDES}
${LRC_SRC_PATH}
${DRING_SRC_PATH})
# output test executable files into test folder
set_target_properties(qml_tests
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/x64/test"
)
# POST_BUILD steps
# check time stamp
set(TIME_STAMP_FILE ".deploy.stamp")
add_custom_command(TARGET qml_tests POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:qml_tests>"
COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE}
-P ${PROJECT_SOURCE_DIR}/cmake/time_stamp_check.cmake)
# daemon deploy
add_custom_command(TARGET qml_tests POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:qml_tests>"
COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE}
-DCOPY_TO_PATH=$<TARGET_FILE_DIR:qml_tests>
-DDRING_PATH=${DRING}
-DPROJECT_ROOT_DIR=${PROJECT_SOURCE_DIR}
-P ${PROJECT_SOURCE_DIR}/cmake/windows_daemon_deploy.cmake)
# Qt deploy for test qmls
add_custom_command(TARGET qml_tests POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:qml_tests>"
COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE}
-DWIN_DEPLOY_QT_PATH=${CMAKE_PREFIX_PATH}/bin
-DQML_SRC_DIR=${CMAKE_SOURCE_DIR}/tests/qml
-DEXE_NAME=$<TARGET_FILE:qml_tests>
-DOFF_SCREEN_PLUGIN_REQUESTED=TRUE
-DCOPY_TO_PATH=$<TARGET_FILE_DIR:qml_tests>/platforms
-DOFF_SCREEN_PLUGIN_PATH=${CMAKE_PREFIX_PATH}/plugins/platforms
-P ${PROJECT_SOURCE_DIR}/cmake/windows_qt_deploy.cmake)
# Qt deploy for src qmls
add_custom_command(TARGET qml_tests POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:qml_tests>"
COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE}
-DWIN_DEPLOY_QT_PATH=${CMAKE_PREFIX_PATH}/bin
-DQML_SRC_DIR=${SRC_DIR}
-DEXE_NAME=$<TARGET_FILE:qml_tests>
-P ${PROJECT_SOURCE_DIR}/cmake/windows_qt_deploy.cmake)
# create time stamp
add_custom_command(TARGET qml_tests POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:qml_tests>"
COMMAND ${CMAKE_COMMAND} -DTIME_STAMP_FILE=${TIME_STAMP_FILE}
-P ${PROJECT_SOURCE_DIR}/cmake/time_stamp_create.cmake)
# Unittests
target_link_libraries(unittests
${QTWRAPPER_LIB}
${RINGCLIENT_STATIC_LIB}
${QRENCODE_LIB}
${GNUTLS_LIB}
${DRING_LIB}
${WINDOWS_SYS_LIBS})
target_include_directories(unittests PUBLIC
${TESTS_INCLUDES}
${LRC_SRC_PATH}
${DRING_SRC_PATH})
# output test executable files into test folder
set_target_properties(unittests
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/x64/test"
)
else()
include_directories(${LRC}/include/libringclient
${LRC}/include
${LIBNM_INCLUDE_DIRS}
${LIBNOTIFY_INCLUDE_DIRS}
${LIBGDKPIXBUF_INCLUDE_DIRS})
# QML tests
target_link_libraries(qml_tests
${ringclient}
${qrencode}
${X11}
${LIBNM_LIBRARIES}
${LIBNOTIFY_LIBRARIES}
${LIBGDKPIXBUF_LIBRARIES})
target_include_directories(qml_tests PUBLIC
${TESTS_INCLUDES}
${LRC}/include/libringclient
${LRC}/include)
add_test(NAME QmlTests COMMAND qml_tests -input ${PROJECT_SOURCE_DIR}/tests/qml/)
# Unittests
target_link_libraries(unittests
${ringclient}
${qrencode}
pthread
${X11}
${LIBNM_LIBRARIES}
${LIBNOTIFY_LIBRARIES}
${LIBGDKPIXBUF_LIBRARIES})
target_include_directories(unittests PUBLIC
${TESTS_INCLUDES}
${LRC}/include/libringclient
${LRC}/include)
add_test(NAME UnitTests COMMAND unittests)
endif()

View file

@ -57,10 +57,6 @@ public:
QFontDatabase::addApplicationFont(":/images/FontAwesome.otf"); QFontDatabase::addApplicationFont(":/images/FontAwesome.otf");
#if defined _MSC_VER && !COMPILE_ONLY
gnutls_global_init();
#endif
lrcInstance_.reset( lrcInstance_.reset(
new LRCInstance(nullptr, nullptr, "", connectivityMonitor_.get(), muteDring_)); new LRCInstance(nullptr, nullptr, "", connectivityMonitor_.get(), muteDring_));
lrcInstance_->subscribeToDebugReceived(); lrcInstance_->subscribeToDebugReceived();

View file

@ -45,10 +45,6 @@ public:
settingsManager.reset(new AppSettingsManager(nullptr)); settingsManager.reset(new AppSettingsManager(nullptr));
systemTray.reset(new SystemTray(settingsManager.get(), nullptr)); systemTray.reset(new SystemTray(settingsManager.get(), nullptr));
#if defined _MSC_VER
gnutls_global_init();
#endif
std::atomic_bool isMigrating(false); std::atomic_bool isMigrating(false);
lrcInstance.reset( lrcInstance.reset(
new LRCInstance(nullptr, nullptr, "", connectivityMonitor.get(), muteDring)); new LRCInstance(nullptr, nullptr, "", connectivityMonitor.get(), muteDring));