mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 13:36:30 +03:00
See https://support.delta.chat/t/discussion-how-to-show-error-states/1363/10 <!-- comment --> It turns out that it's pretty easy to distinguish between lots of states (currently Error/NotConnected, Connecting…, Getting new messages… and Connected). What's not that easy is distinguishing between an actual error and no network, because if the server just doesn't respond, it could mean that we don't have network or that we are trying ipv6, but only ipv4 works. **WRT debouncing:** Sending of EVENT_CONNECTIVITY_CHANGED is not debounced, but emitted every time one of the 3 threads (Inbox, Mvbox and Sentbox) has a network error, starts fetching data, or is done fetching data. This means that it is emitted: - 9 times when dc_maybe_network() is called or we get network connection - 12 times when we lose network connection Some measurements: dc_get_connectivity() takes a little more than 1ms (in my measurements back in March), dc_get_connectivity_html() takes 10-20ms. This means that it's no immmediate problem to call them very often, might increase battery drain though. For the UI it may be a lot of work to update the title everytime; at least Android is smart enough to update the title only once. Possible problems (we don't have to worry about them now I think): - Due to the scan_folders feature, if the user has lots of folders, the state could be "Connecting..." for quite a long time, generally DC seemed a little unresponsive to me because it took so long for "Connecting..." to go away. Telegram has a state "Updating..." that sometimes comes after "Connecting...". To be done in other PRs: - Better handle the case that the password was changed on the server and authenticating fails, see https://github.com/deltachat/deltachat-core-rust/issues/1923 and https://github.com/deltachat/deltachat-core-rust/issues/1768 - maybe event debouncing (except for "Connected" connectivity events) fix https://github.com/deltachat/deltachat-android/issues/1760
208 lines
6.7 KiB
Python
208 lines
6.7 KiB
Python
import distutils.ccompiler
|
|
import distutils.log
|
|
import distutils.sysconfig
|
|
import os
|
|
import platform
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import textwrap
|
|
import types
|
|
|
|
import cffi
|
|
|
|
|
|
def local_build_flags(projdir, target):
|
|
"""Construct build flags for building against a checkout.
|
|
|
|
:param projdir: The root directory of the deltachat-core-rust project.
|
|
:param target: The rust build target, `debug` or `release`.
|
|
"""
|
|
flags = types.SimpleNamespace()
|
|
if platform.system() == 'Darwin':
|
|
flags.libs = ['resolv', 'dl']
|
|
flags.extra_link_args = [
|
|
'-framework', 'CoreFoundation',
|
|
'-framework', 'CoreServices',
|
|
'-framework', 'Security',
|
|
]
|
|
elif platform.system() == 'Linux':
|
|
flags.libs = ['rt', 'dl', 'm']
|
|
flags.extra_link_args = []
|
|
else:
|
|
raise NotImplementedError("Compilation not supported yet on Windows, can you help?")
|
|
target_dir = os.environ.get("CARGO_TARGET_DIR")
|
|
if target_dir is None:
|
|
target_dir = os.path.join(projdir, 'target')
|
|
flags.objs = [os.path.join(target_dir, target, 'libdeltachat.a')]
|
|
assert os.path.exists(flags.objs[0]), flags.objs
|
|
flags.incs = [os.path.join(projdir, 'deltachat-ffi')]
|
|
return flags
|
|
|
|
|
|
def system_build_flags():
|
|
"""Construct build flags for building against an installed libdeltachat."""
|
|
flags = types.SimpleNamespace()
|
|
flags.libs = ['deltachat']
|
|
flags.objs = []
|
|
flags.incs = []
|
|
flags.extra_link_args = []
|
|
return flags
|
|
|
|
|
|
def extract_functions(flags):
|
|
"""Extract the function definitions from deltachat.h.
|
|
|
|
This creates a .h file with a single `#include <deltachat.h>` line
|
|
in it. It then runs the C preprocessor to create an output file
|
|
which contains all function definitions found in `deltachat.h`.
|
|
"""
|
|
distutils.log.set_verbosity(distutils.log.INFO)
|
|
cc = distutils.ccompiler.new_compiler(force=True)
|
|
distutils.sysconfig.customize_compiler(cc)
|
|
tmpdir = tempfile.mkdtemp()
|
|
try:
|
|
src_name = os.path.join(tmpdir, "include.h")
|
|
dst_name = os.path.join(tmpdir, "expanded.h")
|
|
with open(src_name, "w") as src_fp:
|
|
src_fp.write('#include <deltachat.h>')
|
|
cc.preprocess(source=src_name,
|
|
output_file=dst_name,
|
|
include_dirs=flags.incs,
|
|
macros=[('PY_CFFI', '1')])
|
|
with open(dst_name, "r") as dst_fp:
|
|
return dst_fp.read()
|
|
finally:
|
|
shutil.rmtree(tmpdir)
|
|
|
|
|
|
def find_header(flags):
|
|
"""Use the compiler to find the deltachat.h header location.
|
|
|
|
This uses a small utility in deltachat.h to find the location of
|
|
the header file location.
|
|
"""
|
|
distutils.log.set_verbosity(distutils.log.INFO)
|
|
cc = distutils.ccompiler.new_compiler(force=True)
|
|
distutils.sysconfig.customize_compiler(cc)
|
|
tmpdir = tempfile.mkdtemp()
|
|
try:
|
|
src_name = os.path.join(tmpdir, "where.c")
|
|
obj_name = os.path.join(tmpdir, "where.o")
|
|
dst_name = os.path.join(tmpdir, "where")
|
|
with open(src_name, "w") as src_fp:
|
|
src_fp.write(textwrap.dedent("""
|
|
#include <stdio.h>
|
|
#include <deltachat.h>
|
|
|
|
int main(void) {
|
|
printf("%s", _dc_header_file_location());
|
|
return 0;
|
|
}
|
|
"""))
|
|
cwd = os.getcwd()
|
|
try:
|
|
os.chdir(tmpdir)
|
|
cc.compile(sources=["where.c"],
|
|
include_dirs=flags.incs,
|
|
macros=[("PY_CFFI_INC", "1")])
|
|
finally:
|
|
os.chdir(cwd)
|
|
cc.link_executable(objects=[obj_name],
|
|
output_progname="where",
|
|
output_dir=tmpdir)
|
|
return subprocess.check_output(dst_name)
|
|
finally:
|
|
shutil.rmtree(tmpdir)
|
|
|
|
|
|
def extract_defines(flags):
|
|
"""Extract the required #DEFINEs from deltachat.h.
|
|
|
|
Since #DEFINEs are interpreted by the C preprocessor we can not
|
|
use the compiler to extract these and need to parse the header
|
|
file ourselves.
|
|
|
|
The defines are returned in a string that can be passed to CFFIs
|
|
cdef() method.
|
|
"""
|
|
header = find_header(flags)
|
|
defines_re = re.compile(r"""
|
|
\#define\s+ # The start of a define.
|
|
( # Begin capturing group which captures the define name.
|
|
(?: # A nested group which is not captured, this allows us
|
|
# to build the list of prefixes to extract without
|
|
# creation another capture group.
|
|
DC_EVENT
|
|
| DC_QR
|
|
| DC_MSG
|
|
| DC_LP
|
|
| DC_EMPTY
|
|
| DC_CERTCK
|
|
| DC_STATE
|
|
| DC_STR
|
|
| DC_CONTACT_ID
|
|
| DC_GCL
|
|
| DC_GCM
|
|
| DC_SOCKET
|
|
| DC_CHAT
|
|
| DC_PROVIDER
|
|
| DC_KEY_GEN
|
|
| DC_IMEX
|
|
| DC_CONNECTIVITY
|
|
) # End of prefix matching
|
|
_[\w_]+ # Match the suffix, e.g. _RSA2048 in DC_KEY_GEN_RSA2048
|
|
) # Close the capturing group, this contains
|
|
# the entire name e.g. DC_MSG_TEXT.
|
|
\s+\S+ # Ensure there is whitespace followed by a value.
|
|
""", re.VERBOSE)
|
|
defines = []
|
|
with open(header) as fp:
|
|
for line in fp:
|
|
match = defines_re.match(line)
|
|
if match:
|
|
defines.append(match.group(1))
|
|
return '\n'.join('#define {} ...'.format(d) for d in defines)
|
|
|
|
|
|
def ffibuilder():
|
|
projdir = os.environ.get('DCC_RS_DEV')
|
|
if projdir:
|
|
target = os.environ.get('DCC_RS_TARGET', 'release')
|
|
flags = local_build_flags(projdir, target)
|
|
else:
|
|
flags = system_build_flags()
|
|
builder = cffi.FFI()
|
|
builder.set_source(
|
|
'deltachat.capi',
|
|
"""
|
|
#include <deltachat.h>
|
|
int dc_event_has_string_data(int e)
|
|
{
|
|
return DC_EVENT_DATA2_IS_STRING(e);
|
|
}
|
|
""",
|
|
include_dirs=flags.incs,
|
|
libraries=flags.libs,
|
|
extra_objects=flags.objs,
|
|
extra_link_args=flags.extra_link_args,
|
|
)
|
|
builder.cdef("""
|
|
typedef int... time_t;
|
|
void free(void *ptr);
|
|
extern int dc_event_has_string_data(int);
|
|
""")
|
|
function_defs = extract_functions(flags)
|
|
defines = extract_defines(flags)
|
|
builder.cdef(function_defs)
|
|
builder.cdef(defines)
|
|
return builder
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import os.path
|
|
pkgdir = os.path.join(os.path.dirname(__file__), '..')
|
|
builder = ffibuilder()
|
|
builder.compile(tmpdir=pkgdir, verbose=True)
|