| #!/usr/bin/env python3 |
| # |
| # This is the Ring build helper, it can do these things: |
| # - Build Ring |
| # - Install Ring |
| # - Run Ring |
| # |
| |
| import argparse |
| import os |
| import subprocess |
| import sys |
| import time |
| import platform |
| import multiprocessing |
| import shutil |
| |
| IOS_DISTRIBUTION_NAME="ios" |
| OSX_DISTRIBUTION_NAME="osx" |
| ANDROID_DISTRIBUTION_NAME="android" |
| |
| APT_BASED_DISTROS = [ |
| 'debian', |
| 'ubuntu', |
| 'linuxmint', |
| 'raspbian', |
| ] |
| |
| DNF_BASED_DISTROS = [ |
| 'fedora', |
| ] |
| |
| PACMAN_BASED_DISTROS = [ |
| 'arch', |
| ] |
| |
| ZYPPER_BASED_DISTROS = [ |
| 'opensuse', |
| ] |
| |
| APT_INSTALL_SCRIPT = [ |
| 'apt-get update', |
| 'apt-get install -y %(packages)s' |
| ] |
| |
| BREW_UNLINK_SCRIPT = [ |
| 'brew unlink %(packages)s' |
| ] |
| |
| BREW_INSTALL_SCRIPT = [ |
| 'brew update', |
| 'brew install %(packages)s', |
| 'brew link --force --overwrite %(packages)s' |
| ] |
| |
| RPM_INSTALL_SCRIPT = [ |
| 'dnf update', |
| 'dnf install -y %(packages)s' |
| ] |
| |
| PACMAN_INSTALL_SCRIPT = [ |
| 'pacman -Sy', |
| 'pacman -S %(packages)s' |
| ] |
| |
| ZYPPER_INSTALL_SCRIPT = [ |
| 'zypper update', |
| 'zypper install -y %(packages)s' |
| ] |
| |
| ZYPPER_DEPENDENCIES = [ |
| # build system |
| 'autoconf', 'autoconf-archive', 'automake', 'cmake', 'patch', 'gcc-c++', 'libtool', 'which', |
| # contrib dependencies |
| 'curl', 'gzip', 'bzip2', |
| # daemon |
| 'speexdsp-devel', 'speex-devel', 'libdbus-c++-devel', 'jsoncpp-devel', 'yaml-cpp-devel', |
| 'yasm', 'libuuid-devel', 'libnettle-devel', 'libopus-devel', |
| 'libgnutls-devel', 'msgpack-devel', 'libavcodec-devel', 'libavdevice-devel', 'pcre-devel', |
| 'alsa-devel', 'libpulse-devel', 'libudev-devel', 'libva-devel', 'libvdpau-devel', |
| 'libopenssl-devel', |
| # lrc |
| 'libQt5Core-devel', 'libQt5DBus-devel', 'libqt5-linguist-devel', |
| # gnome client |
| 'gtk3-devel', 'clutter-gtk-devel', 'qrencode-devel', |
| 'gettext-tools', 'libnotify-devel', 'libappindicator3-devel', 'webkit2gtk3-devel', |
| 'NetworkManager-devel', 'libcanberra-gtk3-devel' |
| ] |
| |
| MINGW64_FEDORA_DEPENDENCIES = [ |
| 'mingw64-binutils', 'mingw64-gcc', 'mingw64-headers', 'mingw64-crt', 'mingw64-gcc-c++', |
| 'mingw64-pkg-config', 'yasm', 'gettext-devel', 'cmake', 'patch', 'libtool', 'automake', |
| 'autoconf', 'autoconf-archive', 'make', 'xz', 'bzip2', 'which', 'mingw64-qt5-qtbase', |
| 'mingw64-qt5-qttools', 'mingw64-qt5-qtsvg', 'mingw64-qt5-qtwinextras', 'mingw64-libidn', |
| 'mingw64-xz-libs','msgpack-devel' |
| ] |
| |
| MINGW32_FEDORA_DEPENDENCIES = [ |
| 'mingw32-binutils', 'mingw32-gcc', 'mingw32-headers', 'mingw32-crt', 'mingw32-gcc-c++', |
| 'mingw32-pkg-config', 'yasm', 'gettext-devel', 'cmake', 'patch', 'libtool', 'automake', |
| 'autoconf', 'autoconf-archive', 'make', 'xz', 'bzip2', 'which', 'mingw32-qt5-qtbase', |
| 'mingw32-qt5-qttools', 'mingw32-qt5-qtsvg', 'mingw32-qt5-qtwinextras', 'mingw32-libidn', |
| 'mingw32-xz-libs', 'msgpack-devel' |
| ] |
| |
| DNF_DEPENDENCIES = [ |
| 'autoconf', 'autoconf-archive', 'automake', 'cmake', 'speexdsp-devel', 'pulseaudio-libs-devel', |
| 'libtool', 'dbus-devel', 'expat-devel', 'pcre-devel', |
| 'yaml-cpp-devel', 'boost-devel', 'dbus-c++-devel', 'dbus-devel', |
| 'libXext-devel', 'libXfixes-devel', 'yasm', |
| 'speex-devel', 'chrpath', 'check', 'astyle', 'uuid-c++-devel', 'gettext-devel', |
| 'gcc-c++', 'which', 'alsa-lib-devel', 'systemd-devel', 'libuuid-devel', |
| 'uuid-devel', 'gnutls-devel', 'nettle-devel', 'opus-devel', 'speexdsp-devel', |
| 'yaml-cpp-devel', 'qt5-qtbase-devel', 'swig', 'qrencode-devel', 'jsoncpp-devel', |
| 'gtk3-devel', 'clutter-devel', 'clutter-gtk-devel', |
| 'libnotify-devel', 'libappindicator-gtk3-devel', 'patch', 'libva-devel', 'openssl-devel', |
| 'webkitgtk4-devel', 'NetworkManager-libnm-devel', 'libvdpau-devel', 'msgpack-devel', 'libcanberra-devel', |
| 'sqlite-devel', 'openssl-static' |
| ] |
| |
| APT_DEPENDENCIES = [ |
| 'autoconf', 'autoconf-archive', 'autopoint', 'automake', 'cmake', 'dbus', 'doxygen', 'g++', |
| 'gettext', 'gnome-icon-theme-symbolic', 'libasound2-dev', 'libavcodec-dev', |
| 'libavdevice-dev', 'libavformat-dev', 'libboost-dev', |
| 'libclutter-gtk-1.0-dev', 'libcppunit-dev', 'libdbus-1-dev', |
| 'libdbus-c++-dev', 'libebook1.2-dev', 'libexpat1-dev', 'libgnutls28-dev', |
| 'libgtk-3-dev', 'libjack-dev', 'libnotify-dev', |
| 'libopus-dev', 'libpcre3-dev', 'libpulse-dev', 'libssl-dev', |
| 'libspeex-dev', 'libspeexdsp-dev', 'libswscale-dev', 'libtool', |
| 'libudev-dev', 'libyaml-cpp-dev', 'qtbase5-dev', 'libqt5sql5-sqlite', 'sip-tester', 'swig', |
| 'uuid-dev', 'yasm', 'libqrencode-dev', 'libjsoncpp-dev', 'libappindicator3-dev', |
| 'libva-dev', 'libwebkit2gtk-4.0-dev', 'libnm-dev', 'libvdpau-dev', 'libmsgpack-dev', 'libcanberra-gtk3-dev' |
| ] |
| |
| PACMAN_DEPENDENCIES = [ |
| 'autoconf', 'autoconf-archive', 'gettext', 'cmake', 'dbus', 'doxygen', 'gcc', 'gnome-icon-theme-symbolic', |
| 'ffmpeg', 'boost', 'clutter-gtk', 'cppunit', 'libdbus', 'dbus-c++', 'libe-book', |
| 'expat', 'gtk3', 'jack', 'libnotify', 'opus', 'pcre', 'libpulse', |
| 'speex', 'speexdsp', 'libtool', 'yaml-cpp', 'qt5-base', |
| 'swig', 'yasm', 'qrencode', 'make', 'patch', 'pkg-config', |
| 'automake', 'libva', 'webkit2gtk', 'libnm', 'libvdpau', 'libcanberra', 'openssl' |
| ] |
| |
| OSX_DEPENDENCIES = [ |
| 'autoconf', 'cmake', 'gettext', 'pkg-config', 'qt5', |
| 'libtool', 'yasm', 'nasm', 'automake' |
| ] |
| |
| OSX_DEPENDENCIES_UNLINK = [ |
| 'autoconf*', 'cmake*', 'gettext*', 'pkg-config*', 'qt*', 'qt@5.*', |
| 'libtool*', 'yasm*', 'nasm*', 'automake*', 'gnutls*', 'nettle*', 'msgpack*' |
| ] |
| |
| IOS_DEPENDENCIES = [ |
| 'autoconf', 'automake', 'cmake', 'yasm', 'libtool', |
| 'pkg-config', 'gettext', 'swiftlint', 'swiftgen' |
| ] |
| |
| IOS_DEPENDENCIES_UNLINK = [ |
| 'autoconf*', 'automake*', 'cmake*', 'yasm*', 'libtool*', |
| 'pkg-config*', 'gettext*', 'swiftlint*', 'swiftgen*' |
| ] |
| |
| UNINSTALL_SCRIPT = [ |
| 'make -C daemon uninstall', |
| 'rm -rf ./lrc/build-global/', |
| 'rm -rf ./lrc/build-local/', |
| 'rm -rf ./client-gnome/build-global', |
| 'rm -rf ./client-gnome/build-local', |
| ] |
| |
| OSX_UNINSTALL_SCRIPT = [ |
| 'make -C daemon uninstall', |
| 'rm -rf install/client-macosx', |
| ] |
| |
| STOP_SCRIPT = [ |
| 'xargs kill < daemon.pid', |
| 'xargs kill < jami-gnome.pid', |
| ] |
| |
| |
| def run_dependencies(args): |
| if args.distribution in APT_BASED_DISTROS: |
| execute_script(APT_INSTALL_SCRIPT, |
| {"packages": ' '.join(APT_DEPENDENCIES)} |
| ) |
| elif args.distribution in DNF_BASED_DISTROS: |
| execute_script( |
| RPM_INSTALL_SCRIPT, |
| {"packages": ' '.join(DNF_DEPENDENCIES)} |
| ) |
| elif args.distribution == "mingw32": |
| execute_script( |
| RPM_INSTALL_SCRIPT, |
| {"packages": ' '.join(MINGW32_FEDORA_DEPENDENCIES)} |
| ) |
| elif args.distribution == "mingw64": |
| execute_script( |
| RPM_INSTALL_SCRIPT, |
| {"packages": ' '.join(MINGW64_FEDORA_DEPENDENCIES)} |
| ) |
| elif args.distribution in PACMAN_BASED_DISTROS: |
| execute_script( |
| PACMAN_INSTALL_SCRIPT, |
| {"packages": ' '.join(PACMAN_DEPENDENCIES)} |
| ) |
| |
| elif args.distribution in ZYPPER_BASED_DISTROS: |
| execute_script( |
| ZYPPER_INSTALL_SCRIPT, |
| {"packages": ' '.join(ZYPPER_DEPENDENCIES)} |
| ) |
| |
| elif args.distribution == OSX_DISTRIBUTION_NAME: |
| execute_script( |
| BREW_UNLINK_SCRIPT, |
| {"packages": ' '.join(OSX_DEPENDENCIES_UNLINK)}, |
| False |
| ) |
| execute_script( |
| BREW_INSTALL_SCRIPT, |
| {"packages": ' '.join(OSX_DEPENDENCIES)}, |
| False |
| ) |
| |
| elif args.distribution == IOS_DISTRIBUTION_NAME: |
| execute_script( |
| BREW_UNLINK_SCRIPT, |
| {"packages": ' '.join(IOS_DEPENDENCIES_UNLINK)}, |
| False |
| ) |
| execute_script( |
| BREW_INSTALL_SCRIPT, |
| {"packages": ' '.join(IOS_DEPENDENCIES)}, |
| False |
| ) |
| |
| elif args.distribution == ANDROID_DISTRIBUTION_NAME: |
| print("The Android version does not need more dependencies.\nPlease continue with the --install instruction.") |
| sys.exit(1) |
| |
| else: |
| print("Not yet implemented for current distribution (%s)" % args.distribution) |
| sys.exit(1) |
| |
| def run_init(): |
| # Extract modules path from '.gitmodules' file |
| module_names = [] |
| with open('.gitmodules') as fd: |
| for line in fd.readlines(): |
| if line.startswith('[submodule "'): |
| module_names.append(line[line.find('"')+1:line.rfind('"')]) |
| |
| os.system("git submodule update --init") |
| os.system("git submodule foreach 'git checkout master && git pull'") |
| for name in module_names: |
| copy_file("./scripts/commit-msg", ".git/modules/"+name+"/hooks") |
| |
| def copy_file(src, dest): |
| print("Copying:" + src + " to " + dest) |
| try: |
| shutil.copy2(src, dest) |
| # eg. src and dest are the same file |
| except shutil.Error as e: |
| print('Error: %s' % e) |
| # eg. source or destination doesn't exist |
| except IOError as e: |
| print('Error: %s' % e.strerror) |
| |
| def run_install(args): |
| install_args = ' -p ' + str(multiprocessing.cpu_count()) |
| if args.static: |
| install_args += ' -s' |
| if args.global_install: |
| install_args += ' -g' |
| if args.distribution == OSX_DISTRIBUTION_NAME: |
| proc= subprocess.Popen("brew --prefix qt5", shell=True, stdout=subprocess.PIPE) |
| qt5dir = proc.stdout.read() |
| os.environ['CMAKE_PREFIX_PATH'] = str(qt5dir.decode('ascii')) |
| install_args += " -c client-macosx" |
| execute_script(["CONFIGURE_FLAGS='--without-dbus' ./scripts/install.sh " + install_args]) |
| elif args.distribution == IOS_DISTRIBUTION_NAME: |
| os.chdir("./client-ios") |
| execute_script(["./compile-ios.sh"]) |
| elif args.distribution == ANDROID_DISTRIBUTION_NAME: |
| os.chdir("./client-android") |
| execute_script(["./compile.sh"]) |
| elif args.distribution == 'mingw32': |
| os.environ['CMAKE_PREFIX_PATH'] = '/usr/i686-w64-mingw32/sys-root/mingw/lib/cmake' |
| os.environ['QTDIR'] = '/usr/i686-w64-mingw32/sys-root/mingw/lib/qt5/' |
| os.environ['PATH'] = '/usr/i686-w64-mingw32/bin/qt5/:' + os.environ['PATH'] |
| execute_script(["./scripts/win_compile.sh"]) |
| elif args.distribution == 'mingw64': |
| os.environ['CMAKE_PREFIX_PATH'] = '/usr/x86_64-w64-mingw32/sys-root/mingw/lib/cmake' |
| os.environ['QTDIR'] = '/usr/x86_64-w64-mingw32/sys-root/mingw/lib/qt5/' |
| os.environ['PATH'] = '/usr/x86_64-w64-mingw32/bin/qt5/:' + os.environ['PATH'] |
| execute_script(["./scripts/win_compile.sh --arch=64"]) |
| else: |
| if args.distribution in ZYPPER_BASED_DISTROS: |
| os.environ['JSONCPP_LIBS'] = "-ljsoncpp" #fix jsoncpp pkg-config bug, remove when jsoncpp package bumped |
| install_args += ' -c client-gnome' |
| execute_script(["./scripts/install.sh " + install_args]) |
| |
| |
| def run_uninstall(args): |
| if args.distribution == OSX_DISTRIBUTION_NAME: |
| execute_script(OSX_UNINSTALL_SCRIPT) |
| else: |
| execute_script(UNINSTALL_SCRIPT) |
| |
| |
| def run_run(args): |
| if args.distribution == OSX_DISTRIBUTION_NAME: |
| subprocess.Popen(["install/client-macosx/Ring.app/Contents/MacOS/Ring"]) |
| return True |
| |
| run_env = os.environ |
| run_env['LD_LIBRARY_PATH'] = run_env.get('LD_LIBRARY_PATH', '') + ":install/lrc/lib" |
| |
| try: |
| dring_log = open("daemon.log", 'a') |
| dring_log.write('=== Starting daemon (%s) ===' % time.strftime("%d/%m/%Y %H:%M:%S")) |
| dring_process = subprocess.Popen( |
| ["./install/daemon/lib/ring/dring", "-c", "-d"], |
| stdout=dring_log, |
| stderr=dring_log |
| ) |
| |
| with open('daemon.pid', 'w') as f: |
| f.write(str(dring_process.pid)+'\n') |
| |
| client_log = open("jami-gnome.log", 'a') |
| client_log.write('=== Starting client (%s) ===' % time.strftime("%d/%m/%Y %H:%M:%S")) |
| client_process = subprocess.Popen( |
| ["./install/client-gnome/bin/jami-gnome", "-d"], |
| stdout=client_log, |
| stderr=client_log, |
| env=run_env |
| ) |
| |
| with open('jami-gnome.pid', 'w') as f: |
| f.write(str(client_process.pid)+'\n') |
| |
| if args.debug: |
| subprocess.call( |
| ['gdb','-x', 'gdb.gdb', './install/daemon/lib/ring/dring'], |
| ) |
| |
| if args.background == False: |
| dring_process.wait() |
| client_process.wait() |
| |
| except KeyboardInterrupt: |
| print("\nCaught KeyboardInterrupt...") |
| |
| finally: |
| if args.background == False: |
| try: |
| # Only kill the processes if they are running, as they could |
| # have been closed by the user. |
| print("Killing processes...") |
| dring_log.close() |
| if dring_process.poll() is None: |
| dring_process.kill() |
| client_log.close() |
| if client_process.poll() is None: |
| client_process.kill() |
| except UnboundLocalError: |
| # Its okay! We crashed before we could start a process or open a |
| # file. All that matters is that we close files and kill processes |
| # in the right order. |
| pass |
| return True |
| |
| |
| def run_stop(args): |
| execute_script(STOP_SCRIPT) |
| |
| def execute_script(script, settings=None, fail=True): |
| if settings == None: |
| settings = {} |
| for line in script: |
| line = line % settings |
| rv = os.system(line) |
| if rv != 0 and fail == True: |
| print('Error executing script! Exit code: %s' % rv, file=sys.stderr) |
| sys.exit(1) |
| |
| def validate_args(parsed_args): |
| """Validate the args values, exit if error is found""" |
| |
| # Check arg values |
| supported_distros = [ANDROID_DISTRIBUTION_NAME, OSX_DISTRIBUTION_NAME, IOS_DISTRIBUTION_NAME] + APT_BASED_DISTROS + DNF_BASED_DISTROS + PACMAN_BASED_DISTROS + ZYPPER_BASED_DISTROS + ['mingw32','mingw64'] |
| |
| if parsed_args.distribution not in supported_distros: |
| print('Distribution \''+parsed_args.distribution+'\' not supported.\nChoose one of: %s' \ |
| % ', '.join(supported_distros), |
| file=sys.stderr) |
| sys.exit(1) |
| |
| def parse_args(): |
| ap = argparse.ArgumentParser(description="Ring build tool") |
| |
| ga = ap.add_mutually_exclusive_group(required=True) |
| ga.add_argument( |
| '--init', action='store_true', |
| help='Init Ring repository') |
| ga.add_argument( |
| '--dependencies', action='store_true', |
| help='Install ring build dependencies') |
| ga.add_argument( |
| '--install', action='store_true', |
| help='Build and install Ring') |
| ga.add_argument( |
| '--uninstall', action='store_true', |
| help='Uninstall Ring') |
| ga.add_argument( |
| '--run', action='store_true', |
| help='Run the Ring daemon and client') |
| ga.add_argument( |
| '--stop', action='store_true', |
| help='Stop the Ring processes') |
| |
| ap.add_argument('--distribution') |
| ap.add_argument('--static', default=False, action='store_true') |
| ap.add_argument('--global-install', default=False, action='store_true') |
| ap.add_argument('--debug', default=False, action='store_true') |
| ap.add_argument('--background', default=False, action='store_true') |
| |
| parsed_args = ap.parse_args() |
| |
| if (parsed_args.distribution is not None): |
| parsed_args.distribution = parsed_args.distribution.lower() |
| else: |
| parsed_args.distribution = choose_distribution() |
| |
| if parsed_args.distribution in ['mingw32', 'mingw64']: |
| if choose_distribution() != "fedora": |
| print('Windows version must be built on a Fedora distribution (>=23)') |
| sys.exit(1) |
| |
| validate_args(parsed_args) |
| |
| return parsed_args |
| |
| def choose_distribution(): |
| system = platform.system().lower() |
| if system == "linux" or system == "linux2": |
| if os.path.isfile("/etc/arch-release"): |
| return "arch" |
| with open("/etc/os-release") as f: |
| for line in f: |
| k,v = line.split("=") |
| if k.strip() == 'ID': |
| return v.strip().replace('"','').split(' ')[0] |
| elif system == "darwin": |
| return OSX_DISTRIBUTION_NAME |
| |
| return 'Unknown' |
| |
| |
| def main(): |
| parsed_args = parse_args() |
| |
| if parsed_args.dependencies: |
| run_dependencies(parsed_args) |
| |
| elif parsed_args.init: |
| run_init() |
| |
| elif parsed_args.install: |
| run_install(parsed_args) |
| |
| elif parsed_args.uninstall: |
| run_uninstall(parsed_args) |
| |
| elif parsed_args.run: |
| run_run(parsed_args) |
| |
| elif parsed_args.stop: |
| run_stop(parsed_args) |
| |
| |
| if __name__ == "__main__": |
| main() |