blob: 45781b89ac014892d97976ad504be017f81bb1ba [file] [log] [blame]
#!/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
DEBIAN_BASED_DISTROS = [
'Debian',
'Ubuntu',
]
RPM_BASED_DISTROS = [
'Fedora',
]
PACMAN_BASED_DISTROS = [
'Arch Linux',
]
SUSE_BASED_DISTROS = [
'openSUSE',
]
APT_INSTALL_SCRIPT = [
'apt-get update',
'apt-get install -y %(packages)s'
]
BREW_INSTALL_SCRIPT = [
'brew update',
'brew install -y %(packages)s',
'brew link --force gettext'
]
RPM_INSTALL_SCRIPT = [
'sudo dnf update',
'sudo dnf install -y %(packages)s'
]
PACMAN_INSTALL_SCRIPT = [
'sudo pacman -Sy',
'sudo pacman -S %(packages)s'
]
ZYPPER_INSTALL_SCRIPT = [
'sudo zypper update',
'sudo zypper install -y %(packages)s'
]
OPENSUSE_DEPENDENCIES = [
# build system
'autoconf', 'automake', 'cmake', 'patch', 'gcc-c++', 'libtool',
# daemon
'speexdsp-devel', 'speex-devel', 'libdbus-c++-devel', 'jsoncpp-devel', 'yaml-cpp-devel',
'libupnp-devel', 'boost-devel', 'yasm', 'libuuid-devel', 'libsamplerate-devel',
'libnettle-devel', 'libopus-devel', 'libgnutls-devel', 'msgpack-devel', 'libavcodec-devel',
'libavdevice-devel', 'pcre-devel', 'libogg-devel', 'libsndfile-devel', 'libvorbis-devel',
'flac-devel', 'libgsm-devel', 'alsa-devel', 'libpulse-devel', 'libudev-devel',
# lrc
'libQt5Core-devel', 'libQt5DBus-devel', 'libqt5-linguist-devel',
# gnome client
'gtk3-devel', 'clutter-gtk-devel', 'qrencode-devel', 'evolution-data-server-devel',
'gettext-tools', 'libnotify-devel', 'libappindicator3-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',
'make', 'xz', 'bzip2', 'which', 'mingw64-qt5-qtbase', 'mingw64-qt5-qttools',
'mingw64-qt5-qtsvg', 'mingw64-qt5-qtwinextras'
]
MINGW32_FEDORA_DEPENDENCIES = [
'mingw32-binutils', 'mingw32-gcc', 'mingw32-headers', 'mingw32-crt', 'mingw32-gcc-c++',
'mingw32-pkg-config', 'yasm', 'gettext-devel', 'cmake', 'patch', 'libtool', 'automake',
'make', 'xz', 'bzip2', 'which', 'mingw32-qt5-qtbase', 'mingw32-qt5-qttools',
'mingw32-qt5-qtsvg', 'mingw32-qt5-qtwinextras'
]
FEDORA_DEPENDENCIES = [
'autoconf', 'automake', 'cmake', 'speexdsp-devel', 'pulseaudio-libs-devel',
'libsamplerate-devel', 'libtool', 'dbus-devel', 'expat-devel', 'pcre-devel',
'yaml-cpp-devel', 'boost-devel', 'dbus-c++-devel', 'dbus-devel',
'libsndfile-devel', 'libXext-devel', 'libXfixes-devel', 'yasm',
'speex-devel', 'chrpath', 'check', 'astyle', 'uuid-c++-devel', 'gettext',
'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', 'evolution-data-server-devel',
'libnotify-devel', 'libappindicator-gtk3-devel', 'patch'
]
UBUNTU_DEPENDENCIES = [
'autoconf', 'autopoint', 'cmake', 'dbus', 'doxygen', 'g++', 'gettext',
'gnome-icon-theme-symbolic', 'libasound2-dev', 'libavcodec-dev',
'libavcodec-extra', 'libavdevice-dev', 'libavformat-dev', 'libboost-dev',
'libclutter-gtk-1.0-dev', 'libcppunit-dev', 'libdbus-1-dev',
'libdbus-c++-dev', 'libebook1.2-dev', 'libexpat1-dev', 'libgnutls-dev',
'libgsm1-dev', 'libgtk-3-dev', 'libjack-dev', 'libnotify-dev',
'libopus-dev', 'libpcre3-dev', 'libpulse-dev', 'libsamplerate0-dev',
'libsndfile1-dev', 'libspeex-dev', 'libspeexdsp-dev', 'libswscale-dev', 'libtool',
'libudev-dev', 'libupnp-dev', 'libyaml-cpp-dev', 'qtbase5-dev', 'sip-tester', 'swig',
'uuid-dev', 'yasm', 'libqrencode-dev', 'libjsoncpp-dev', 'libappindicator3-dev'
]
DEBIAN_DEPENDENCIES = [
'autoconf', 'autopoint', 'cmake', 'dbus', 'doxygen', 'g++', 'gettext',
'gnome-icon-theme-symbolic', 'libasound2-dev', 'libavcodec-dev',
'libavcodec-extra', 'libavdevice-dev', 'libavformat-dev', 'libboost-dev',
'libclutter-gtk-1.0-dev', 'libcppunit-dev', 'libdbus-1-dev',
'libdbus-c++-dev', 'libebook1.2-dev', 'libexpat1-dev', 'libgnutls-dev',
'libgsm1-dev', 'libgtk-3-dev', 'libjack-dev', 'libnotify-dev',
'libopus-dev', 'libpcre3-dev', 'libpulse-dev', 'libsamplerate0-dev',
'libsndfile1-dev', 'libspeex-dev', 'libspeexdsp-dev', 'libswscale-dev', 'libtool',
'libudev-dev', 'libupnp-dev', 'libyaml-cpp-dev', 'qtbase5-dev', 'sip-tester', 'swig',
'uuid-dev', 'yasm', 'libqrencode-dev', 'libjsoncpp-dev', 'libappindicator3-dev'
]
ARCH_LINUX_DEPENDENCIES = [
'autoconf', 'gettext', 'cmake', 'dbus', 'doxygen', 'gcc', 'gnome-icon-theme-symbolic',
'ffmpeg', 'boost', 'clutter-gtk', 'cppunit', 'libdbus', 'dbus-c++', 'libe-book',
'expat', 'gsm', 'gtk3', 'jack', 'libnotify', 'opus', 'pcre', 'libpulse', 'libsamplerate',
'libsndfile', 'speex', 'speexdsp', 'libtool', 'libupnp', 'yaml-cpp', 'qt5-base',
'swig', 'yasm', 'qrencode', 'evolution-data-server', 'make', 'patch', 'pkg-config',
'automake'
]
OSX_DEPENDENCIES = [
'autoconf', 'cmake', 'gettext', 'pkg-config', 'homebrew/versions/qt55',
'libtool', 'yasm', 'automake'
]
UNINSTALL_SCRIPT = [
'make -C daemon uninstall',
'xargs rm < lrc/build-global/install_manifest.txt',
'xargs rm < client-gnome/build-global/install_manifest.txt',
]
OSX_UNINSTALL_SCRIPT = [
'make -C daemon uninstall',
'rm -rf install/client-macosx',
]
STOP_SCRIPT = [
'xargs kill < daemon.pid',
'xargs kill < gnome-ring.pid',
]
def run_dependencies(args):
if args.distribution == "Ubuntu":
execute_script(APT_INSTALL_SCRIPT,
{"packages": ' '.join(UBUNTU_DEPENDENCIES)}
)
elif args.distribution == "Debian":
execute_script(
APT_INSTALL_SCRIPT,
{"packages": ' '.join(DEBIAN_DEPENDENCIES)}
)
elif args.distribution == "Fedora":
execute_script(
RPM_INSTALL_SCRIPT,
{"packages": ' '.join(FEDORA_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 == "Arch Linux":
execute_script(
PACMAN_INSTALL_SCRIPT,
{"packages": ' '.join(ARCH_LINUX_DEPENDENCIES)}
)
elif args.distribution == "openSUSE":
execute_script(
ZYPPER_INSTALL_SCRIPT,
{"packages": ' '.join(OPENSUSE_DEPENDENCIES)}
)
elif args.distribution == "OSX":
execute_script(
BREW_INSTALL_SCRIPT,
{"packages": ' '.join(OSX_DEPENDENCIES)}
)
elif args.distribution == "Android":
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():
os.system("git submodule update --init")
os.system("git submodule foreach 'git checkout master && git pull'")
for project in ["daemon", "lrc", "client-macosx", "client-gnome", "client-android"]:
copy_file("./scripts/commit-msg", ".git/modules/"+project+"/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":
proc= subprocess.Popen("brew --prefix homebrew/versions/qt55", 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 == "Android":
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 == "openSUSE":
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":
execute_script(OSX_UNINSTALL_SCRIPT)
else:
execute_script(UNINSTALL_SCRIPT)
def run_run(args):
if args.distribution == "OSX":
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("gnome-ring.log", 'a')
client_log.write('=== Starting client (%s) ===' % time.strftime("%d/%m/%Y %H:%M:%S"))
client_process = subprocess.Popen(
["./install/client-gnome/bin/gnome-ring", "-d"],
stdout=client_log,
stderr=client_log,
env=run_env
)
with open('gnome-ring.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):
if settings == None:
settings = {}
for line in script:
line = line % settings
rv = os.system(line)
if rv != 0:
print('Error executing script! Exit code: %s' % rv,
file=sys.stderr)
return False
return True
def validate_args(parsed_args):
"""Validate the args values, exit if error is found"""
# Check arg values
supported_distros = ['Android', 'Ubuntu', 'Debian', 'OSX', 'Fedora', 'Arch Linux', 'openSUSE', 'Automatic', 'mingw32', 'mingw64']
if parsed_args.distribution not in supported_distros:
print('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', default='Automatic')
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 == 'Automatic':
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":
with open("/etc/os-release") as f:
for line in f:
k,v = line.split("=")
if k.strip() == 'NAME':
return v.strip().replace('"','')
elif system == "darwin":
return 'OSX'
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()