Скрипт Python не блокируется, пока подпроцесс сбрасывает стандартный вывод в реальном времени - PullRequest
0 голосов
/ 09 июля 2019

Как часть процесса сборки моего проекта, я написал простой скрипт, который открывает канал подпроцесса для выполнения команды make, что в конечном итоге приведет к созданию файла tar, прежде чем в конечном итоге опубликовать полученный tar в нашем каталоге сборки. , Сценарий выглядит следующим образом:

#!/usr/bin/env python3
import os, shutil, utils

# Set Environment Variables
utils.setupEnvironment()

if not os.path.isdir(os.environ["BUILD_PKGDEST"]):
    os.makedirs(os.environ["BUILD_PKGDEST"])

utils.executeBuild(["make", "soltr_load", "CCACHE_WRITE=%s" % (os.environ["CCACHE_WRITE"])])
utils.publish("production", "soltr_%s.tar.gz" % (utils.getVersionInfo()))

Как вы можете видеть, он вызывает две функции util, executeBuild и publish, которые выглядят следующим образом:

def executeBuild(command):
        p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        for line in iter(p.stdout.readline, b''):
                print (line.decode("utf-8").rstrip())
        p.communicate()

def execute(command):
        return subprocess.check_output(command).decode("UTF-8").rstrip()

def publish(pathToPublish, infile, outfile=None):
        if outfile is None: outfile = infile

        ...

        md5sum = execute(["md5sum", "%s/%s" % (ENV["BUILD_PKGDEST"], infile)])

        ...

Код, насколько я понимаю, довольно прост. Во время executeBuild подпроцесс откроет канал и выполнит мою команду make, в то время как последующий цикл for перебирает свой вывод и выводит его на консоль в реальном времени. P.communicate () блокирует выполнение сценариев до тех пор, пока канал не будет закрыт, после чего будет запущен шаг публикации, и оба создадут md5sum и опубликуют оба файла.

Моя проблема в том, что шаг публикации запускается случайным образом, пока команда make все еще выполняется. Иногда это занимает 3 минуты, иногда 15 минут. Команда make в среднем должна выполняться на 20. Вот пример моего журнала ошибок.

14:00:23 ********************************************************************************
14:00:23 Original command follows:
14:00:23 ********************************************************************************
14:00:23 /opt/soldev/bin/ccd-gcc_v2 --ccd-distcc --ccd-cc /opt/soldev/toolchains/v1/targets/centos7-i686/bin/i686-target-linux-gnu-g++ -fpic -Wno-deprecated -Wignored-qualifiers -D_GLIBCXX_PERMIT_BACKWARD_HASH -mmmx -msse -m64 -march=x86-64 -Wa,--64 -DARCH=LINUX_X86_64 -fPIC -DSOLDEV_ENTERPRISE_EDITION -msse2 -g -rdynamic -Wall -Wextra -Wpointer-arith -Wwrite-strings -Werror -pipe -fexceptions -pthread -D_REENTRANT -O2 -Wno-strict-aliasing -fno-strict-aliasing -Woverloaded-virtual -Wno-format-extra-args -fmessage-length=0 -std=gnu++0x -D__STDC_FORMAT_MACROS -D_GLIBCXX_USE_CXX11_ABI=0 -
...
I/opt/soldev/targets/centos7-i686/usr/lib/glib-2.0/include -I/opt/cvsdirs/loadbuild/jenkins/slave/workspace/<redacted>/vrs32/base/obj_Linux-x86_64-centos7_opt -I//opt/cvsdirs/loadbuild/jenkins/slave/workspace/<redacted>/vrs32/solcbr/common/hw/src/bladeAdbSimulated.cpp -o common/hw/src/bladeAdbSimulated.omd5sum: /opt/cvsdirs/loadbuild/jenkins/slave/workspace/<redacted>/vrs32/solcbr/obj_Linux-x86_64-centos7_lm_vmr_opt/build_artifact/soltr_100.0<redacted>2.0.63.tar.gz: No such file or directory
14:00:35 
14:00:35 /opt/cvsdirs/loadbuild/jenkins/slave/workspace/<redacted>/vrs32/solcbr/Rules.mk:173: recipe for target 'common/hw/src/bladeAdbSimulated.o' failed
14:00:35 make[3]: *** [common/hw/src/bladeAdbSimulated.o] Error 1
14:00:35 make[3]: *** Waiting for unfinished jobs....
14:00:35 make[2]: *** [obj_Linux-x86_64-centos7_lm_opt] Error 2
14:00:35 build/target.mk:53: recipe for target 'obj_Linux-x86_64-centos7_lm_opt' failed
14:00:35 Makefile:26: recipe for target 'appload' failed
14:00:35 make[1]: *** [appload] Error 2
14:00:35 make[1]: Leaving directory '/opt/cvsdirs/loadbuild/jenkins/slave/workspace/<redacted>/vrs32/solcbr'
14:00:35 __________Warning: 1 pump-mode compilation(s) failed on server, but succeeded locally.
14:00:35 __________Distcc-pump was demoted to plain mode.  See the Distcc Discrepancy Symptoms section in the include_server(1) man page.
14:00:35 __________Shutting down distcc-pump include server
14:00:35 make: *** [soltr_load] Error 2
14:00:35 make: *** [soltr_load] Error 2
14:00:35 Traceback (most recent call last):
14:00:35   File "build-env/loadbuild/build_lm.py", line 11, in <module>
14:00:35     utils.publish("production", "soltr_%s.tar.gz" % (utils.getVersionInfo()))
14:00:35   File "/opt/sbox/loadbuild/jenkins/slave/workspace/<redacted>/vrs32/build-env/loadbuild/utils.py", line 82, in publish
14:00:35     md5sum = execute(["md5sum", "%s/%s" % (ENV["BUILD_PKGDEST"], infile)])
14:00:35   File "/opt/sbox/loadbuild/jenkins/slave/workspace/<redacted>/vrs32/build-env/loadbuild/utils.py", line 5, in execute
14:00:35     return subprocess.check_output(command).decode("UTF-8").rstrip()
14:00:35   File "/usr/lib64/python3.6/subprocess.py", line 336, in check_output
14:00:35     **kwargs).stdout
14:00:35   File "/usr/lib64/python3.6/subprocess.py", line 418, in run
14:00:35     output=stdout, stderr=stderr)
14:00:35 subprocess.CalledProcessError: Command '['md5sum', '/opt/cvsdirs/loadbuild/jenkins/slave/workspace/<redacted>/vrs32/solcbr/obj_Linux-x86_64-centos7_lm_vmr_opt/build_artifact/soltr_100.0<redacted>2.0.63.tar.gz']' returned non-zero exit status 1.
14:00:35 Build step 'Execute shell' marked build as failure

Журнал ошибок очень запутан, но лучше всего я могу его интерпретировать, он перечисляет оригинальную команду (которая является командой, которая компилирует и создает код), и в самом конце вы можете видеть добавленный md5sum: ... до конца команды.

Он взрывается, потому что команда md5sum хочет файл tar, который не генерируется, потому что команда make не завершена. Это возвращает ошибку и приводит к сбою исходной команды, которая все еще выполнялась. Однако он не должен даже достигать команды md5sum (которая находится на этапе публикации) до тех пор, пока начальный канал не будет завершен из-за блокировки канала обратно в executeBuild.

Мое лучшее предположение относительно того, что происходит, заключается в том, что цикл for, который перебирает выходные данные канала, достигает своего конца и по какой-то причине движется дальше, но я не знаю, почему это происходит, потому что мое понимание p.communicate() было то, что он блокирует движение скрипта, пока канал не будет закрыт. Если по какой-то причине он выходит из цикла for до завершения команды, он все равно должен быть заблокирован, пока не закончится; это просто ничего не печатало бы.

Есть ли способ для меня это исправить? Везде, где я читаю, говорится, что метод, который я использую, - лучший способ распечатать стандартный вывод в реальном времени, поэтому мне интересно, есть ли альтернативы.

...