Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package seafile for openSUSE:Factory checked 
in at 2021-06-29 22:42:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/seafile (Old)
 and      /work/SRC/openSUSE:Factory/.seafile.new.2625 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "seafile"

Tue Jun 29 22:42:49 2021 rev:8 rq:902681 version:8.0.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/seafile/seafile.changes  2021-06-01 
10:38:55.188961990 +0200
+++ /work/SRC/openSUSE:Factory/.seafile.new.2625/seafile.changes        
2021-06-29 22:42:51.710842130 +0200
@@ -1,0 +2,5 @@
+Sun Jun 27 19:06:20 UTC 2021 - Christophe Giboudeaux <[email protected]>
+
+- Update to 8.0.3. No changelog.
+
+-------------------------------------------------------------------

Old:
----
  v8.0.2.tar.gz

New:
----
  v8.0.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ seafile.spec ++++++
--- /var/tmp/diff_new_pack.WuYJYC/_old  2021-06-29 22:42:52.286842890 +0200
+++ /var/tmp/diff_new_pack.WuYJYC/_new  2021-06-29 22:42:52.290842895 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           seafile
-Version:        8.0.2
+Version:        8.0.3
 Release:        0
 Summary:        Cloud storage client
 License:        GPL-2.0-only

++++++ v8.0.2.tar.gz -> v8.0.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/common/fs-mgr.c 
new/seafile-8.0.3/common/fs-mgr.c
--- old/seafile-8.0.2/common/fs-mgr.c   2021-05-27 03:58:11.000000000 +0200
+++ new/seafile-8.0.3/common/fs-mgr.c   2021-06-24 08:03:18.000000000 +0200
@@ -390,6 +390,16 @@
             seaf_warning ("Failed to rename %s to %s: %s. "
                           "Failed to move backup file to conflict file.\n",
                           backup_path, conflict_path, strerror(errno));
+            if (mtime > 0) {
+                /*
+                 * Set the checked out file mtime to what it has to be.
+                 */
+                if (seaf_set_file_time (file_path, mtime) < 0) {
+                    seaf_warning ("Failed to set mtime for %s.\n", file_path);
+                }
+            }
+            //Don't delete local file when failed to rename backup file to 
conflict file.
+            goto out;
         }
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/configure.ac 
new/seafile-8.0.3/configure.ac
--- old/seafile-8.0.2/configure.ac      2021-05-27 03:58:11.000000000 +0200
+++ new/seafile-8.0.3/configure.ac      2021-06-24 08:03:18.000000000 +0200
@@ -2,7 +2,7 @@
 
 
 AC_PREREQ(2.61)
-AC_INIT([seafile], [8.0.2], [[email protected]])
+AC_INIT([seafile], [8.0.3], [[email protected]])
 AC_CONFIG_HEADER([config.h])
 
 AC_CONFIG_MACRO_DIR([m4])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/daemon/http-tx-mgr.c 
new/seafile-8.0.3/daemon/http-tx-mgr.c
--- old/seafile-8.0.2/daemon/http-tx-mgr.c      2021-05-27 03:58:11.000000000 
+0200
+++ new/seafile-8.0.3/daemon/http-tx-mgr.c      2021-06-24 08:03:18.000000000 
+0200
@@ -38,10 +38,12 @@
 
 #define HTTP_OK 200
 #define HTTP_BAD_REQUEST 400
+#define HTTP_REQUEST_TIME_OUT 408
 #define HTTP_FORBIDDEN 403
 #define HTTP_NOT_FOUND 404
 #define HTTP_NO_QUOTA 443
 #define HTTP_REPO_DELETED 444
+#define HTTP_REPO_TOO_LARGE 447
 #define HTTP_REPO_CORRUPTED 445
 #define HTTP_INTERNAL_SERVER_ERROR 500
 
@@ -1089,6 +1091,8 @@
         return SYNC_ERROR_ID_SERVER_REPO_DELETED;
     else if (status == HTTP_REPO_CORRUPTED)
         return SYNC_ERROR_ID_SERVER_REPO_CORRUPT;
+    else if (status == HTTP_REPO_TOO_LARGE || status == HTTP_REQUEST_TIME_OUT)
+        return SYNC_ERROR_ID_LIBRARY_TOO_LARGE;
     else
         return SYNC_ERROR_ID_GENERAL_ERROR;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/daemon/seafile-error.c 
new/seafile-8.0.3/daemon/seafile-error.c
--- old/seafile-8.0.2/daemon/seafile-error.c    2021-05-27 03:58:11.000000000 
+0200
+++ new/seafile-8.0.3/daemon/seafile-error.c    2021-06-24 08:03:18.000000000 
+0200
@@ -169,6 +169,11 @@
         SYNC_ERROR_LEVEL_FILE,
         "File or directory is invalid on Windows"
     },
+    {
+        SYNC_ERROR_ID_LIBRARY_TOO_LARGE,
+        SYNC_ERROR_LEVEL_REPO,
+        "Library is too large to sync"
+    },
 };
 
 const char *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/debian/changelog 
new/seafile-8.0.3/debian/changelog
--- old/seafile-8.0.2/debian/changelog  2021-05-27 03:58:11.000000000 +0200
+++ new/seafile-8.0.3/debian/changelog  2021-06-24 08:03:18.000000000 +0200
@@ -1,3 +1,8 @@
+seafile-daemon (8.0.3) unstable; urgency=low
+
+  * new upstream release
+
+ -- Jonathan Xu <[email protected]>  Tue, 24 Jun  2021 11:26:24 +0800
 seafile-daemon (8.0.2) unstable; urgency=low
 
   * new upstream release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/include/seafile-error.h 
new/seafile-8.0.3/include/seafile-error.h
--- old/seafile-8.0.2/include/seafile-error.h   2021-05-27 03:58:11.000000000 
+0200
+++ new/seafile-8.0.3/include/seafile-error.h   2021-06-24 08:03:18.000000000 
+0200
@@ -61,6 +61,7 @@
 #define SYNC_ERROR_ID_NO_ERROR                  29
 #define SYNC_ERROR_ID_REMOVE_UNCOMMITTED_FOLDER 30
 #define SYNC_ERROR_ID_INVALID_PATH_ON_WINDOWS   31
-#define N_SYNC_ERROR_ID                         32
+#define SYNC_ERROR_ID_LIBRARY_TOO_LARGE         32
+#define N_SYNC_ERROR_ID                         33
 
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/msi/Includes.wxi 
new/seafile-8.0.3/msi/Includes.wxi
--- old/seafile-8.0.2/msi/Includes.wxi  2021-05-27 03:58:11.000000000 +0200
+++ new/seafile-8.0.3/msi/Includes.wxi  2021-06-24 08:03:18.000000000 +0200
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Include Id="SeafileInclude">
-  <?define CurrentSeafileVersion="8.0.2" ?>
+  <?define CurrentSeafileVersion="8.0.3" ?>
 
   <!-- Update Guid ????????? -->
   <?define CurrentUpdateGuid="65DED1C8-A5F1-4C49-8E7E-B0A8A5A6535C" ?>
@@ -145,7 +145,8 @@
   <!-- <?define ProductGuid="D21D1108-878F-43D9-A3A2-39F4B38A5694" ?> (7.0.10) 
-->
   <!-- <?define ProductGuid="CE032912-6DCF-4C5F-A447-AC303D0530A7" ?> (8.0.0) 
-->
   <!-- <?define ProductGuid="93B20E38-EFF9-4CD8-BFF4-F0DF21DEADB8" ?> (8.0.1) 
-->
-  <?define ProductGuid="8F8A6FBA-EAD1-4B16-B074-03C1BA4E1109" ?>
+  <!-- <?define ProductGuid="8F8A6FBA-EAD1-4B16-B074-03C1BA4E1109" ?> (8.0.2) 
-->
+  <?define ProductGuid="82A1C721-6B4E-473B-B515-8D0B9242C6A6" ?>
 
   <?define GuidOfCustomComponent="AD201805-3CBD-4834-9097-5D934F7E0000" ?>
   <?define GuidOfAutoStartComponent="AD201805-3CBD-4834-9097-5D934F7E0001" ?>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/scripts/build/build-mac-local.py 
new/seafile-8.0.3/scripts/build/build-mac-local.py
--- old/seafile-8.0.2/scripts/build/build-mac-local.py  1970-01-01 
01:00:00.000000000 +0100
+++ new/seafile-8.0.3/scripts/build/build-mac-local.py  2021-06-24 
08:03:18.000000000 +0200
@@ -0,0 +1,884 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+'''This script builds the seafile mac client.
+'''
+
+import atexit
+import logging
+import commands
+from contextlib import contextmanager
+import glob
+import multiprocessing
+import optparse
+import os
+from os.path import abspath, basename, dirname, join, exists, expanduser
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+
+FINAL_APP = 'Seafile Client.app'
+FSPLUGIN_APPEX_NAME = 'Seafile FinderSync.appex'
+CERT_ID = '79AB1AF435DD2CBC5FDB3EBBD45B0DA17727B299'
+
+BUILDDIR = join(os.getcwd(), "../../../")
+
+####################
+### Requires Python 2.6+
+####################
+if sys.version_info[0] == 3:
+    print 'Python 3 not supported yet. Quit now.'
+    sys.exit(1)
+if sys.version_info[1] < 6:
+    print 'Python 2.6 or above is required. Quit now.'
+    sys.exit(1)
+
+####################
+### Global variables
+####################
+# command line configuartion
+conf = {}
+
+# key names in the conf dictionary.
+# pylint: disable=bad-whitespace
+CONF_VERSION            = 'version'
+CONF_NO_STRIP           = 'nostrip'
+CONF_BRAND              = 'brand'
+
+NUM_CPU = multiprocessing.cpu_count()
+PID = os.getpid()
+
+####################
+### Common helper functions
+####################
+def highlight(content, is_error=False):
+    '''Add ANSI color to content to get it highlighted on terminal'''
+    if is_error:
+        return '\x1b[1;31m%s\x1b[m' % content
+    else:
+        return '\x1b[1;32m%s\x1b[m' % content
+
+def info(msg):
+    logging.info(highlight('[INFO][{}] ').format(msg))
+
+@contextmanager
+def cd(path):
+    oldpwd = os.getcwd()
+    os.chdir(path)
+    try:
+        yield
+    finally:
+        os.chdir(oldpwd)
+
+def exist_in_path(prog):
+    '''Test whether prog exists in system path'''
+    return bool(find_in_path(prog))
+
+def prepend_env_value(name, value, seperator=':'):
+    '''append a new value to a list'''
+    try:
+        current_value = os.environ[name]
+    except KeyError:
+        current_value = ''
+
+    new_value = value
+    if current_value:
+        new_value += seperator + current_value
+
+    os.environ[name] = new_value
+
+def find_in_path(prog):
+    '''Find a file in system path'''
+    dirs = os.environ['PATH'].split(':')
+    for d in dirs:
+        if d == '':
+            continue
+        path = join(d, prog)
+        if exists(path):
+            return path
+
+    return None
+
+def error(msg=None, usage=None):
+    if msg:
+        print highlight('[ERROR] ') + msg
+    if usage:
+        print usage
+    sys.exit(1)
+
+def run_argv(argv, cwd=None, env=None, suppress_stdout=False, 
suppress_stderr=False):
+    '''Run a program and wait it to finish, and return its exit code. The
+    standard output of this program is supressed.
+
+    '''
+    info('running %s, cwd=%s' % (' '.join(['"{}"'.format(a) for a in argv]), 
cwd or os.getcwd()))
+    with open(os.devnull, 'w') as devnull:
+        if suppress_stdout:
+            stdout = devnull
+        else:
+            stdout = sys.stdout
+
+        if suppress_stderr:
+            stderr = devnull
+        else:
+            stderr = sys.stderr
+
+        proc = subprocess.Popen(argv,
+                                cwd=cwd,
+                                stdout=stdout,
+                                stderr=stderr,
+                                env=env)
+        return proc.wait()
+
+def run(cmdline, cwd=None, env=None, suppress_stdout=False, 
suppress_stderr=False):
+    '''Like run_argv but specify a command line string instead of argv'''
+    info('running %s, cwd=%s' % (cmdline, cwd or os.getcwd()))
+    with open(os.devnull, 'w') as devnull:
+        if suppress_stdout:
+            stdout = devnull
+        else:
+            stdout = sys.stdout
+
+        if suppress_stderr:
+            stderr = devnull
+        else:
+            stderr = sys.stderr
+
+        proc = subprocess.Popen(cmdline,
+                                cwd=cwd,
+                                stdout=stdout,
+                                stderr=stderr,
+                                env=env,
+                                shell=True)
+        return proc.wait()
+
+def must_run(cmdline, *a, **kw):
+    ret = run(cmdline, *a, **kw)
+    if ret != 0:
+        error('failed to run %s' % cmdline)
+
+def check_remove(path):
+    """
+    Remove the file/dir specified by `path`, if exists.
+    """
+    path = abspath(path)
+    assert path.count('/') >= 2
+    if exists(path):
+        is_dir = os.path.isdir(path)
+        info('removing {} {}'.format('dir' if is_dir else 'file', path))
+        if is_dir:
+            shutil.rmtree(path)
+        else:
+            os.unlink(path)
+
+def must_mkdir(path):
+    '''Create a directory, exit on failure'''
+    try:
+        if not exists(path):
+            os.mkdir(path)
+    except OSError, e:
+        error('failed to create directory %s:%s' % (path, e))
+
+def must_copy(src, dst):
+    '''Copy src to dst, exit on failure'''
+    try:
+        shutil.copy(src, dst)
+    except Exception, e:
+        error('failed to copy %s to %s: %s' % (src, dst, e))
+
+def must_copytree(src, dst):
+    '''Copy src tree to dst, exit on failure'''
+    try:
+        shutil.copytree(src, dst)
+    except Exception, e:
+        error('failed to copy %s to %s: %s' % (src, dst, e))
+
+def check_project_version(version):
+    '''A valid version must be like 1.2.2, 1.3'''
+    if not re.match(r'^[0-9]+(\.[0-9]+)+$', version):
+        error('%s is not a valid version' % version, usage="build-mac-local.py 
8.0.0")
+
+def check_cmd_para():
+    args = sys.argv
+    if len(args) != 2:
+        error('The number of parameters is incorrect', 
usage="build-mac-local.py 8.0.0")
+    global version
+    version = args[1]
+    check_project_version(version)
+
+
+class Project(object):
+    '''Base class for a project'''
+    # Probject name, i.e. libseaprc/seafile/
+    name = ''
+
+    # A list of shell commands to configure/build the project
+    build_commands = []
+
+    def __init__(self):
+        # the path to pass to --prefix=/<prefix>
+        self.prefix = join(BUILDDIR, 'usr')
+        # project dir, like <builddir>/seafile/
+        self.projdir = join(BUILDDIR, '{}' .format(self.name))
+
+    def get_version(self):
+        # libsearpc can have different versions from seafile.
+        raise NotImplementedError
+
+    def get_source_commit_id(self):
+        '''By convetion, we record the commit id of the source code in the
+        file "<projdir>/latest_commit"
+
+        '''
+        latest_commit_file = join(self.projdir, 'latest_commit')
+        with open(latest_commit_file, 'r') as fp:
+            commit_id = fp.read().strip('\n\r\t ')
+
+        return commit_id
+
+    def append_cflags(self, macros):
+        cflags = ' '.join(['-D%s=%s' % (k, macros[k]) for k in macros])
+        prepend_env_value('CPPFLAGS',
+                          cflags,
+                          seperator=' ')
+
+    def before_build(self):
+        '''Hook method to do project-specific stuff before running build 
commands'''
+        pass
+
+    def build(self):
+        '''Build the source'''
+        self.before_build()
+        info('Building %s' % self.name)
+        for cmd in self.build_commands:
+            if run(cmd, cwd=self.projdir) != 0:
+                error('error when running command:\n\t%s\n' % cmd)
+
+def concurrent_make():
+    return 'make -j%s' % NUM_CPU
+
+class Libsearpc(Project):
+    name = 'libsearpc'
+
+    def __init__(self):
+        Project.__init__(self)
+        self.build_commands = [
+            './autogen.sh',
+            './configure --prefix=%s --disable-compile-demo' % self.prefix,
+            concurrent_make(),
+            'make install'
+        ]
+
+class Seafile(Project):
+    name = 'seafile'
+    def __init__(self):
+        Project.__init__(self)
+        self.build_commands = [
+            './autogen.sh',
+            './configure --prefix=%s --disable-fuse' % self.prefix,
+            concurrent_make(),
+            'make install'
+        ]
+
+    def update_cli_version(self):
+        '''Substitute the version number in seaf-cli'''
+        cli_py = join(self.projdir, 'app', 'seaf-cli')
+        with open(cli_py, 'r') as fp:
+            lines = fp.readlines()
+
+        ret = []
+        for line in lines:
+            old = '''SEAF_CLI_VERSION = ""'''
+            new = '''SEAF_CLI_VERSION = "%s"''' %conf[CONF_VERSION]
+            line = line.replace(old, new)
+            ret.append(line)
+
+        with open(cli_py, 'w') as fp:
+            fp.writelines(ret)
+
+    def before_build(self):
+        self.update_cli_version()
+        macros = {}
+        # SET SEAFILE_SOURCE_COMMIT_ID, so it can be printed in the log
+        # macros['SEAFILE_SOURCE_COMMIT_ID'] = '\\"%s\\"' % 
self.get_source_commit_id()
+        # self.append_cflags(macros)
+
+class SeafileClient(Project):
+
+    name = 'seafile-client'
+
+    def __init__(self):
+        Project.__init__(self)
+        cmake_defines = {
+            'CMAKE_OSX_ARCHITECTURES': 'x86_64',
+            'CMAKE_OSX_DEPLOYMENT_TARGET': '10.9',
+            'CMAKE_BUILD_TYPE': 'Release',
+            'BUILD_SHIBBOLETH_SUPPORT': 'ON',
+            'BUILD_SPARKLE_SUPPORT': 'ON',
+        }
+        cmake_defines_formatted = ' '.join(['-D{}={}'.format(k, v) for k, v in 
cmake_defines.iteritems()])
+        self.build_commands = [
+            'rm -f CMakeCache.txt',
+            'cmake -GXcode {}'.format(cmake_defines_formatted),
+            'xcodebuild -target seafile-applet -configuration Release -jobs 
{}'.format(NUM_CPU),
+            'rm -rf seafile-applet.app',
+            'cp -r Release/seafile-applet.app seafile-applet.app',
+            'mkdir -p seafile-applet.app/Contents/Frameworks',
+            'macdeployqt seafile-applet.app',
+        ]
+
+    def before_build(self):
+        pass
+
+class SeafileDMGLayout(Seafile):
+
+    def __init__(self):
+        Seafile.__init__(self)
+        self.build_commands = [
+        ]
+
+class SeafileFinderSyncPlugin(SeafileClient):
+
+    def __init__(self):
+        SeafileClient.__init__(self)
+        self.build_commands = [
+            'fsplugin/build.sh'
+        ]
+
+def prepare_builddir(builddir):
+    must_mkdir(builddir)
+
+    # if not conf[CONF_KEEP]:
+    #     def remove_builddir():
+    #         '''Remove the builddir when exit'''
+    #         info('remove builddir before exit')
+    #         shutil.rmtree(builddir, ignore_errors=True)
+    #     atexit.register(remove_builddir)
+
+    os.chdir(builddir)
+
+    must_mkdir(join(builddir, 'usr'))
+
+def parse_args():
+    parser = optparse.OptionParser()
+    def long_opt(opt):
+        return '--' + opt
+
+    parser.add_option(long_opt(CONF_VERSION),
+                      dest=CONF_VERSION,
+                      nargs=1,
+                      help='the version to build. Must be digits delimited by 
dots, like 1.3.0')
+
+    parser.add_option(long_opt(CONF_NO_STRIP),
+                      dest=CONF_NO_STRIP,
+                      action='store_true',
+                      help='''do not strip debug symbols''')
+
+    parser.add_option(long_opt(CONF_BRAND),
+                      dest=CONF_BRAND,
+                      help='the brand')
+
+    usage = parser.format_help()
+    options, remain = parser.parse_args()
+    if remain:
+        error(usage=usage)
+
+    validate_args(usage, options)
+
+def validate_args(usage, options):
+    required_args = [
+        CONF_VERSION,
+        CONF_NO_STRIP,
+        CONF_BRAND,
+    ]
+
+    # fist check required args
+    for optname in required_args:
+        if getattr(options, optname, None) is None:
+            error('%s must be specified' % optname, usage=usage)
+
+    def get_option(optname):
+        return getattr(options, optname)
+
+    # [ version ]
+    def check_project_version(version):
+        '''A valid version must be like 1.2.2, 1.3'''
+        if not re.match(r'^[0-9]+(\.[0-9]+)+$', version):
+            error('%s is not a valid version' % version, usage=usage)
+
+    version = get_option(CONF_VERSION)
+    check_project_version(version)
+
+    # [ no strip]
+    nostrip = get_option(CONF_NO_STRIP)
+
+    brand = get_option(CONF_BRAND)
+
+    conf[CONF_VERSION] = version
+    conf[CONF_NO_STRIP] = nostrip
+    conf[CONF_BRAND] = brand
+
+def setup_build_env():
+    '''Setup environment variables, such as export PATH=$BUILDDDIR/bin:$PATH'''
+    # os.environ.update({
+    # })
+    prefix = join(BUILDDIR, 'usr')
+
+    prepend_env_value('CFLAGS',
+                      '-Wall -O2 -g -DNDEBUG -I/opt/local/include 
-mmacosx-version-min=10.7',
+                      seperator=' ')
+
+    prepend_env_value('CXXFLAGS',
+                      '-Wall -O2 -g -DNDEBUG -I/opt/local/include 
-mmacosx-version-min=10.7',
+                      seperator=' ')
+
+    prepend_env_value('CPPFLAGS',
+                      '-I%s' % join(prefix, 'include'),
+                      seperator=' ')
+
+    prepend_env_value('CPPFLAGS',
+                      '-DSEAFILE_CLIENT_VERSION=\\"%s\\"' % conf[CONF_VERSION],
+                      seperator=' ')
+
+    if conf[CONF_NO_STRIP]:
+        prepend_env_value('CPPFLAGS',
+                          '-g -O0',
+                          seperator=' ')
+
+    prepend_env_value('LDFLAGS',
+                      '-L%s' % join(prefix, 'lib'),
+                      seperator=' ')
+
+    prepend_env_value('LDFLAGS',
+                      '-L%s' % join(prefix, 'lib64'),
+                      seperator=' ')
+
+    prepend_env_value('LDFLAGS',
+                      '-L/opt/local/lib -Wl,-headerpad_max_install_names 
-mmacosx-version-min=10.7',
+                      seperator=' ')
+
+    prepend_env_value('PATH', join(prefix, 'bin'))
+    prepend_env_value('PKG_CONFIG_PATH', join(prefix, 'lib', 'pkgconfig'))
+    prepend_env_value('PKG_CONFIG_PATH', join(prefix, 'lib64', 'pkgconfig'))
+
+path_pattern = re.compile(r'^\s+(\S+)\s+\(compatibility.*')
+def get_dependent_libs(executable):
+    def is_syslib(lib):
+        if lib.startswith('/usr/lib'):
+            return True
+        if lib.startswith('/System/'):
+            return True
+        return False
+
+    otool_output = commands.getoutput('otool -L %s' % executable)
+    libs = set()
+    for line in otool_output.splitlines():
+        m = path_pattern.match(line)
+        if not m:
+            continue
+        path = m.group(1)
+        if not is_syslib(path):
+            libs.add(path)
+    return libs
+
+def copy_shared_libs():
+    '''copy shared c libs, such as libevent, glib'''
+    builddir = BUILDDIR
+    frameworks_dir = join(SeafileClient().projdir, 
'seafile-applet.app/Contents/Frameworks')
+
+    must_mkdir(frameworks_dir)
+    seafile_path = join(builddir, 'usr/bin/seaf-daemon')
+
+    libs = set()
+    libs.update(get_dependent_libs(seafile_path))
+
+    # Get deps of deps recursively until no more deps can be included
+    while True:
+        newlibs = set(libs)
+        for lib in libs:
+            newlibs.update(get_dependent_libs(lib))
+        if newlibs == libs:
+            break
+        libs = newlibs
+
+    for lib in libs:
+        dst_file = join(frameworks_dir, basename(lib))
+        if exists(dst_file):
+            continue
+        info('Copying %s' % lib)
+        shutil.copy(lib, frameworks_dir)
+
+    change_rpaths()
+
+def change_rpaths():
+    """
+    Chagne the rpath of the referenced dylibs so the app can be relocated
+    anywhere in the user's system.
+    See:
+      - https://blogs.oracle.com/dipol/entry/dynamic_libraries_rpath_and_mac
+      - 
https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/RunpathDependentLibraries.html
+    """
+    contents_dir = join(SeafileClient().projdir, 'seafile-applet.app/Contents')
+    frameworks_dir = join(contents_dir, 'Frameworks')
+    resources_dir = join(contents_dir, 'Resources')
+    macos_dir = join(contents_dir, 'MacOS')
+
+    seafile_applet = join(macos_dir, 'seafile-applet')
+    binaries = [
+        seafile_applet,
+        join(resources_dir, 'seaf-daemon')
+    ]
+
+    RPATH_RE = re.compile(r'^path\s+(\S+)\s+\(offset .*$')
+    def get_rpaths(fn):
+        rpaths = []
+        output = commands.getoutput('otool -l {} | grep -A2 RPATH || 
true'.format(fn))
+        # The output is like
+            #           cmd LC_RPATH
+            #       cmdsize 24
+            #          path /usr/local (offset 12)
+            # --
+            #           cmd LC_RPATH
+            #       cmdsize 48
+            #          path @executable_path/../Frameworks (offset 12)
+        for line in output.splitlines():
+            m = RPATH_RE.match(line.strip())
+            if m:
+                rpaths.append(m.group(1))
+        return rpaths
+
+    def has_rpath(fn, path):
+        return path in get_rpaths(fn)
+
+    def add_frameworks_dir_to_rpath(fn, executable=True):
+        relpath = 'executable_path' if executable else 'loader_path'
+        relative_frameworks_dir = '@{}/../Frameworks'.format(relpath)
+        if not has_rpath(fn, relative_frameworks_dir):
+            must_run('install_name_tool -add_rpath {} {}' 
.format(relative_frameworks_dir, fn))
+
+    def remove_local_rpaths(fn):
+        local_paths = ['/usr/local/lib', '/opt/local/lib']
+        for path in local_paths:
+            if has_rpath(fn, path):
+                must_run('install_name_tool -delete_rpath {} {}'.format(path, 
fn))
+
+    def change_deps_rpath(fn):
+        deps = get_dependent_libs(fn)
+        for dep in deps:
+            bn = basename(dep)
+            if 'framework' in bn or bn.startswith('Qt') or bn == 'Sparkle':
+                continue
+            must_run('install_name_tool -change {} @rpath/{} {}'.format(dep, 
basename(dep), fn))
+
+    for binary in binaries:
+        add_frameworks_dir_to_rpath(binary)
+        remove_local_rpaths(binary)
+        change_deps_rpath(binary)
+
+    libs = os.listdir(frameworks_dir)
+    for lib_name in libs:
+        lib = join(frameworks_dir, lib_name)
+        if os.path.isdir(lib):
+            continue
+        must_run('install_name_tool -id @rpath/{} {}'.format(basename(lib), 
lib))
+        add_frameworks_dir_to_rpath(lib, executable=False)
+        change_deps_rpath(lib)
+        remove_local_rpaths(lib)
+
+DROPDMG = 
'/Applications/DropDMG.app/Contents/Frameworks/DropDMGFramework.framework/Versions/A/dropdmg'
+
+def gen_dmg():
+    output_dmg = 'app-{}.dmg'.format(conf[CONF_VERSION])
+    parentdir = 'app-{}'.format(conf[CONF_VERSION])
+    appdir = join(parentdir, 'seafile-applet.app')
+    app_plugins_dir = join(appdir, 'Contents/PlugIns')
+
+    layout = SeafileDMGLayout()
+    layout_folder = join(layout.projdir, 'dmg/seafileLayout')
+
+    args = [
+        DROPDMG,
+        parentdir,
+        '--format', 'bzip2',
+        '--layout-folder', layout_folder,
+        '--volume-name', conf[CONF_BRAND] or 'Seafile Client',
+    ]
+
+    with cd(BUILDDIR):
+        check_remove(parentdir)
+        check_remove(output_dmg)
+        must_mkdir(parentdir)
+        must_run('tar xf seafile-applet.app.tar.gz -C {}'.format(parentdir))
+        # Remove the Qt Bearer plugin because it would run a background thread
+        # to scan wifi networks. See
+        # 
https://trello.com/c/j28eOIo1/359-explicitly-exclude-qt-bearer-plugins-for-the-windows-and-mac-packages
+        # for details.
+        must_run('rm -rf "{}"'.format(join(app_plugins_dir, 'bearer')))
+        # fsplugin must be copied before we sign the final .app dir
+        copy_fsplugin(app_plugins_dir)
+        sign_files(appdir)
+
+        # Rename the .app dir to 'Seafile Client.app', and create the shortcut
+        # to '/Applications' so the user can drag into it when opening the DMG.
+        brand = conf.get('CONF_BRAND')
+        if brand:
+            final_app = '{}.app'.format(brand)
+        else:
+            final_app = FINAL_APP
+        must_run('mv {}/seafile-applet.app "{}/{}"'.format(parentdir, 
parentdir, final_app))
+        must_run('ln -sf /Applications {}/'.format(parentdir))
+
+        # Open DropDMG manually, or dropdmg command line may fail.
+        run(''' osascript -e 'tell application "DropDMG" to quit' || true ''')
+        run(''' osascript -e 'open application "DropDMG"' || true ''')
+        run(''' osascript -e 'activate application "DropDMG"' || true ''')
+
+        # Sometimes DropDmg would fail if there are two many Finder windows.
+        run(''' osascript -e 'tell application "Finder" to close every window' 
''')
+        if run_argv(args) != 0:
+            error('failed to run {}'.format(args))
+
+def sign_in_parallel(files_to_sign):
+    import threading
+    import Queue
+    queue = Queue.Queue()
+    POISON_PILL = ''
+
+    class SignThread(threading.Thread):
+        def __init__(self, index):
+            threading.Thread.__init__(self)
+            self.index = index
+
+        def run(self):
+            info('sign thread {} started'.format(self.index))
+            while True:
+                try:
+                    fn = queue.get(timeout=1)
+                except Queue.Empty:
+                    continue
+                else:
+                    if fn == POISON_PILL:
+                        break
+                    else:
+                        do_sign(fn)
+            info('sign thread {} stopped'.format(self.index))
+
+    TOTAL_THREADS = max(NUM_CPU / 2, 1)
+    threads = []
+    for i in xrange(TOTAL_THREADS):
+        t = SignThread(i)
+        t.start()
+        threads.append(t)
+
+    for fn in files_to_sign:
+        queue.put(fn)
+
+    for _ in xrange(TOTAL_THREADS):
+        queue.put(POISON_PILL)
+
+    for i in xrange(TOTAL_THREADS):
+        threads[i].join()
+
+def sign_files(appdir):
+    webengine_app = join(
+        appdir,
+        
'Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers/QtWebEngineProcess.app'
+    )
+
+    def _glob(pattern, *a, **kw):
+        return glob.glob(join(appdir, pattern), *a, **kw)
+
+    # The webengine app must be signed first, otherwise the sign of
+    # QtWebengineCore.framework would fail.
+    if exists(webengine_app):
+        entitlements = join(Seafile().projdir, 
'scripts/build/osx.entitlements')
+        do_sign(
+            webengine_app,
+            extra_args=['--entitlements', entitlements]
+        )
+
+    # Strip the get-task-allow entitlements for Sparkle binaries
+    for fn in 
_glob('Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/*'):
+        do_sign(fn, preserve_entitlemenets=False)
+
+    # Sign the nested contents of Sparkle before we sign
+    # Sparkle.Framework in the thread pool.
+    for fn in (
+            
'Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app',
+            'Contents/Frameworks/Sparkle.framework/Versions/A/Sparkle',
+    ):
+        do_sign(join(appdir, fn))
+
+    patterns = [
+        'Contents/Frameworks/*.framework',
+        'Contents/PlugIns/*/*.dylib',
+        'Contents/Frameworks/*.dylib',
+        'Contents/Resources/seaf-daemon',
+        'Contents/MacOS/seadrive-gui',
+    ]
+
+    files_to_sign = []
+    for p in patterns:
+        files_to_sign.extend(_glob(p))
+
+    info('{} files to sign'.format(len(files_to_sign)))
+
+    sign_in_parallel(files_to_sign)
+    # for fn in files_to_sign:
+    #     do_sign(fn)
+
+    do_sign(appdir)
+    # do_sign(appdir, extra_args=['--deep'])
+
+_keychain_unlocked = False
+def unlock_keychain():
+    """
+    Unlock the keychain when we're using ssh instead of using the terminal from
+    GUI. See http://stackoverflow.com/a/20208104/1467959
+    """
+    global _keychain_unlocked
+    if not _keychain_unlocked:
+        _keychain_unlocked = True
+        run('security -v unlock-keychain -p vagrant || true')
+
+def do_sign(path, extra_args=None, preserve_entitlemenets=True):
+    unlock_keychain()
+    args = [
+        'codesign',
+        '--verbose=4',
+        '-o', 'runtime',
+        '--timestamp',
+        '--verify',
+        # '--no-strict',
+        '--force',
+        '-s', CERT_ID,
+    ]
+    if preserve_entitlemenets:
+        args += ['--preserve-metadata=entitlements']
+    extra_args = extra_args or []
+    if extra_args:
+        args.extend(extra_args)
+
+    args.append(path)
+
+    if run_argv(args) != 0:
+        error('failed to sign {}'.format(path))
+
+def copy_dmg():
+    brand = 'seafile-client'
+    branded_dmg = '{}-{}.dmg'.format(brand, conf[CONF_VERSION])
+    src_dmg = os.path.join(BUILDDIR, 'app-{}.dmg'.format(conf[CONF_VERSION]))
+    dst_dmg = os.path.join(BUILDDIR, branded_dmg)
+
+    # move msi to outputdir
+    must_copy(src_dmg, dst_dmg)
+
+    print '---------------------------------------------'
+    print 'The build is successfully. Output is:'
+    print '>>\t%s' % dst_dmg
+    print '---------------------------------------------'
+
+def notarize_dmg():
+    pkg = os.path.join(BUILDDIR, 'app-{}.dmg'.format(conf[CONF_VERSION]))
+    info('Try to notarize {}'.format(pkg))
+    notarize_script = join(Seafile().projdir, 'scripts/build/notarize.sh')
+    cmdline = '{} {}'.format(notarize_script, pkg)
+    ret = run(cmdline)
+    if ret != 0:
+        error('failed to notarize: %s' % cmdline)
+    info('Successfully notarized {}'.format(pkg))
+
+def build_and_sign_fsplugin():
+    """
+    Build and sign the fsplugin. The final output would be 
"${buildder}/Seafile FinderSync.appex"
+    """
+    fsplugin = SeafileFinderSyncPlugin()
+    fsplugin.build()
+    with cd(fsplugin.projdir):
+        appex_src = 'fsplugin/{}'.format(FSPLUGIN_APPEX_NAME)
+        appex_dst = join(BUILDDIR, basename(appex_src))
+
+        check_remove(appex_dst)
+        must_copytree(appex_src, appex_dst)
+
+        entitlements_src = 'fsplugin/seafile-fsplugin.entitlements'
+        entitlements_dst = join(BUILDDIR, basename(entitlements_src))
+
+        check_remove(entitlements_dst)
+        must_copy(entitlements_src, entitlements_dst)
+
+    do_sign(
+        appex_dst,
+        extra_args=['--entitlements', entitlements_dst]
+    )
+
+def copy_fsplugin(plugins_dir):
+    src = join(BUILDDIR, FSPLUGIN_APPEX_NAME)
+    dst = join(plugins_dir, FSPLUGIN_APPEX_NAME)
+    check_remove(dst)
+    must_copytree(src, dst)
+
+def copy_sparkle_framework():
+    src = '/usr/local/Sparkle.framework'
+    dst = join(SeafileClient().projdir, 
'seafile-applet.app/Contents/Frameworks', basename(src))
+    check_remove(dst)
+    # Here we use the `cp` command instead of shutil to do the copy, because 
`cp
+    # -P` would keep symlinks as is.
+    must_run('cp -R -P "{}" "{}"'.format(src, dst))
+
+def build_projects():
+    prepare_builddir(BUILDDIR)
+    libsearpc = Libsearpc()
+    seafile = Seafile()
+    seafile_client = SeafileClient()
+
+    libsearpc.build()
+
+    seafile.build()
+
+    seafile_client.build()
+
+    copy_sparkle_framework()
+
+    copy_shared_libs()
+
+def local_workflow():
+    build_projects()
+    generate_app_tar_gz()
+
+    build_and_sign_fsplugin()
+    gen_dmg()
+    notarize_dmg()
+    copy_dmg()
+
+
+def generate_app_tar_gz():
+    output_app_tgz = join(BUILDDIR, 'seafile-applet.app.tar.gz')
+    with cd(SeafileClient().projdir):
+        run('tar czf {} seafile-applet.app'.format(output_app_tgz))
+
+def setup_logging(level=logging.INFO):
+    kw = {
+        'format': '[%(asctime)s][%(module)s]: %(message)s',
+        'datefmt': '%m/%d/%Y %H:%M:%S',
+        'level': level,
+        'stream': sys.stdout
+    }
+
+    logging.basicConfig(**kw)
+    logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(
+        logging.WARNING)
+
+def main():
+    setup_logging()
+    parse_args()
+    # check_cmd_para()
+    info('{} script started'.format(abspath(__file__)))
+    info('NUM_CPU = {}'.format(NUM_CPU))
+    setup_build_env()
+    local_workflow()
+
+if __name__ == '__main__':
+    main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/scripts/build/build-mac.py 
new/seafile-8.0.3/scripts/build/build-mac.py
--- old/seafile-8.0.2/scripts/build/build-mac.py        2021-05-27 
03:58:11.000000000 +0200
+++ new/seafile-8.0.3/scripts/build/build-mac.py        2021-06-24 
08:03:18.000000000 +0200
@@ -221,13 +221,6 @@
     except Exception, e:
         error('failed to copy %s to %s: %s' % (src, dst, e))
 
-def must_rmtree(path):
-    '''Recurse rm dir, exit on failure'''
-    try:
-        shutil.rmtree(path)
-    except Exception as e:
-        error('failed rm dir %s : %s' % (path, e))
-
 class Project(object):
     '''Base class for a project'''
     # Probject name, i.e. libseaprc/seafile/
@@ -795,17 +788,6 @@
         must_run('rm -rf "{}"'.format(join(app_plugins_dir, 'bearer')))
         # fsplugin must be copied before we sign the final .app dir
         copy_fsplugin(app_plugins_dir)
-
-        # Delete  
~/seafpkg/build/seafile-mac-build/app-8.0.1/seafile-applet.app/Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers/QtWebEngineProcess.app
-
-        # Copy new QtWebEngineCoreProcess.app from Qt dir
-        # 
~/Qt/5.15.1/clang_64/lib/QtWebEngineCore.framework/Versions/5/Helpers/QtWebEngineProcess.app
-
-        webengineprocess_path = join(appdir, 
'Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers/QtWebEngineProcess.app')
-        QT_WEBENGINEPROCESS_PATH = 
'/Users/vagrant/Qt/5.15.1/clang_64/lib/QtWebEngineCore.framework/Versions/5/Helpers/QtWebEngineProcess.app'
-        must_rmtree(webengineprocess_path)
-        must_copytree(QT_WEBENGINEPROCESS_PATH, webengineprocess_path)
-
         sign_files(appdir)
 
         # Rename the .app dir to 'Seafile Client.app', and create the shortcut
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/seafile-8.0.2/seafile.vcxproj 
new/seafile-8.0.3/seafile.vcxproj
--- old/seafile-8.0.2/seafile.vcxproj   2021-05-27 03:58:11.000000000 +0200
+++ new/seafile-8.0.3/seafile.vcxproj   2021-06-24 08:03:18.000000000 +0200
@@ -92,7 +92,7 @@
       <WarningLevel>Level3</WarningLevel>
       <SDLCheck>
       </SDLCheck>
-      
<PreprocessorDefinitions>WIN32;UNICODE;WIN32_LEAN_AND_MEAN;SEAFILE_CLIENT;PACKAGE_VERSION="8.0.2";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      
<PreprocessorDefinitions>WIN32;UNICODE;WIN32_LEAN_AND_MEAN;SEAFILE_CLIENT;PACKAGE_VERSION="8.0.3";%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>false</ConformanceMode>
       <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
       
<AdditionalIncludeDirectories>$(ProjectDir)..\libsearpc\lib;$(ProjectDir)common;$(ProjectDir)lib;$(ProjectDir)include;$(ProjectDir)daemon;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -110,7 +110,7 @@
       <WarningLevel>Level1</WarningLevel>
       <SDLCheck>
       </SDLCheck>
-      
<PreprocessorDefinitions>WIN32;UNICODE;WIN32_LEAN_AND_MEAN;SEAFILE_CLIENT;PACKAGE_VERSION="8.0.2";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      
<PreprocessorDefinitions>WIN32;UNICODE;WIN32_LEAN_AND_MEAN;SEAFILE_CLIENT;PACKAGE_VERSION="8.0.3";%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>false</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       
<AdditionalIncludeDirectories>$(ProjectDir)..\libsearpc\lib;$(ProjectDir)common;$(ProjectDir)include;$(ProjectDir)daemon;$(ProjectDir)lib;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -131,7 +131,7 @@
       <IntrinsicFunctions>false</IntrinsicFunctions>
       <SDLCheck>
       </SDLCheck>
-      
<PreprocessorDefinitions>WIN32;PACKAGE_VERSION="8.0.2";WIN32_LEAN_AND_MEAN;UNICODE;SEAFILE_CLIENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      
<PreprocessorDefinitions>WIN32;PACKAGE_VERSION="8.0.3";WIN32_LEAN_AND_MEAN;UNICODE;SEAFILE_CLIENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>false</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       
<AdditionalIncludeDirectories>$(ProjectDir)..\libsearpc\lib;$(ProjectDir);$(ProjectDir)daemon;$(ProjectDir)include;$(ProjectDir)lib;$(ProjectDir)common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -158,7 +158,7 @@
       <IntrinsicFunctions>false</IntrinsicFunctions>
       <SDLCheck>
       </SDLCheck>
-     
<PreprocessorDefinitions>WIN32;PACKAGE_VERSION="8.0.2";WIN32_LEAN_AND_MEAN;UNICODE;SEAFILE_CLIENT;ENABLE_BREAKPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+     
<PreprocessorDefinitions>WIN32;PACKAGE_VERSION="8.0.3";WIN32_LEAN_AND_MEAN;UNICODE;SEAFILE_CLIENT;ENABLE_BREAKPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ConformanceMode>false</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       
<AdditionalIncludeDirectories>$(ProjectDir)..\libsearpc\lib;$(ProjectDir);$(ProjectDir)common;$(ProjectDir)lib;$(ProjectDir)include;$(ProjectDir)daemon;$(ProjectDir)..\breakpad\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

Reply via email to