From e1f736eaad53ed627646653c63cdd58149603822 Mon Sep 17 00:00:00 2001 From: Ruslan Kuprieiev Date: Mon, 17 Dec 2018 15:03:55 +0200 Subject: [PATCH] daemon: fix daemon when running in binary mode When running from binary, we need to fixup environment variables before launching new processes. We also need to detach from the parent tty, because otherwise it causes problems for PyInstaller. Plus, when binary is built as --onefile it still creates pop-up terminal on windows, so we need to switch to --onedir. Fixes #1446 Fixes #1444 Signed-off-by: Ruslan Kuprieiev --- dvc/daemon.py | 42 ++++++++++++++++++++++++++----------- dvc/utils.py | 2 +- scripts/build_posix.sh | 16 ++++++++++++-- scripts/build_windows.cmd | 2 +- scripts/innosetup/setup.iss | 2 +- 5 files changed, 47 insertions(+), 17 deletions(-) diff --git a/dvc/daemon.py b/dvc/daemon.py index 3ae15147d..46cd7adf7 100644 --- a/dvc/daemon.py +++ b/dvc/daemon.py @@ -1,27 +1,42 @@ import os import sys from subprocess import Popen + from dvc.logger import logger +from dvc.utils import is_binary, fix_env class Daemon(object): # pragma: no cover def _spawn_windows(self, cmd): + from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW + CREATE_NEW_PROCESS_GROUP = 0x00000200 DETACHED_PROCESS = 0x00000008 creationflags = CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS - Popen(cmd, - close_fds=True, - shell=False, - creationflags=creationflags) + + startupinfo = STARTUPINFO() + startupinfo.dwFlags |= STARTF_USESHOWWINDOW + + p = Popen(cmd, + env=fix_env(), + close_fds=True, + shell=False, + creationflags=creationflags, + startupinfo=startupinfo) + + p.communicate() def _spawn_posix(self, cmd): + # NOTE: using os._exit instead of sys.exit, because dvc built + # with PyInstaller has trouble with SystemExit exeption and throws + # errors such as "[26338] Failed to execute script __main__" try: pid = os.fork() if pid > 0: return except OSError as exc: logger.error("Failed at first fork", exc) - sys.exit(1) + os._exit(1) os.setsid() os.umask(0) @@ -29,22 +44,25 @@ class Daemon(object): # pragma: no cover try: pid = os.fork() if pid > 0: - sys.exit(0) + os._exit(0) except OSError as exc: logger.error("Failed at second fork", exc) - sys.exit(1) + os._exit(1) sys.stdin.close() sys.stdout.close() sys.stderr.close() - Popen(cmd, - close_fds=True, - shell=False) + p = Popen(cmd, + env=fix_env(), + close_fds=True, + shell=False) + + p.communicate() + + os._exit(0) def __call__(self, args): - from dvc.utils import is_binary - cmd = [sys.executable] if not is_binary(): cmd += ['-m', 'dvc'] diff --git a/dvc/utils.py b/dvc/utils.py index b43160d4d..ec07698de 100644 --- a/dvc/utils.py +++ b/dvc/utils.py @@ -177,7 +177,7 @@ def is_binary(): # NOTE: Fix env variables modified by PyInstaller # http://pyinstaller.readthedocs.io/en/stable/runtime-information.html -def fix_env(env): +def fix_env(env=None): if env is None: env = os.environ.copy() else: diff --git a/scripts/build_posix.sh b/scripts/build_posix.sh index 2b6b9dce1..ee30067cb 100755 --- a/scripts/build_posix.sh +++ b/scripts/build_posix.sh @@ -13,6 +13,7 @@ fi BUILD_DIR=build BIN_DIR=$BUILD_DIR/$INSTALL_DIR/bin +LIB_DIR=$BUILD_DIR/$INSTALL_DIR/lib print_error() { @@ -40,7 +41,7 @@ fpm_build() { print_info "Building $1..." VERSION=$(python -c "import dvc; from dvc import VERSION; print(str(VERSION))") - fpm -s dir -f -t $1 $FPM_FLAGS -n dvc -v $VERSION -C $BUILD_DIR $INSTALL_DIR + fpm -s dir -f -t $1 $FPM_FLAGS -n dvc -v $VERSION -C $BUILD_DIR usr } cleanup() @@ -78,7 +79,18 @@ install_dependencies() build_dvc() { print_info "Building dvc binary..." - pyinstaller --onefile --additional-hooks-dir $(pwd)/scripts/hooks dvc/__main__.py --name dvc --distpath $BIN_DIR --specpath $BUILD_DIR + pyinstaller \ + --additional-hooks-dir $(pwd)/scripts/hooks dvc/__main__.py \ + --name dvc \ + --distpath $LIB_DIR \ + --specpath $BUILD_DIR + + $LIB_DIR/dvc/dvc --help + + mkdir -p $BIN_DIR + pushd $BIN_DIR + ln -s ../lib/dvc/dvc dvc + popd $BIN_DIR/dvc --help } diff --git a/scripts/build_windows.cmd b/scripts/build_windows.cmd index 8b1502850..7c38380df 100644 --- a/scripts/build_windows.cmd +++ b/scripts/build_windows.cmd @@ -19,7 +19,7 @@ call pip install -r requirements.txt || goto :error call pip install pyinstaller || goto :error echo ====== Building dvc binary... ====== -call pyinstaller --onefile --additional-hooks-dir scripts\hooks dvc/__main__.py --name dvc --specpath build +call pyinstaller --additional-hooks-dir scripts\hooks dvc/__main__.py --name dvc --specpath build echo ====== Copying additional files... ====== copy scripts\innosetup\addSymLinkPermissions.ps1 dist\ || goto :error diff --git a/scripts/innosetup/setup.iss b/scripts/innosetup/setup.iss index 35b3fae1a..1bff3a246 100644 --- a/scripts/innosetup/setup.iss +++ b/scripts/innosetup/setup.iss @@ -2,7 +2,7 @@ #define MyAppVersion ReadIni(".\scripts\innosetup\config.ini", "Version", "version", "unknown") #define MyAppPublisher "Dmitry Petrov" #define MyAppURL "https://dataversioncontrol.com/" -#define MyAppDir "..\..\dist" +#define MyAppDir "..\..\dist\dvc" [Setup] AppId={{8258CE8A-110E-4E0D-AE60-FEE00B15F041}