Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH] Support PORTAGE_LOG_FILTER_COMMAND (bug 709746)
Date: Sun, 16 Feb 2020 21:01:57
Message-Id: 20200216205803.150335-1-zmedico@gentoo.org
1 This variable specifies a command that filters stdout and stderr of
2 build logs.
3
4 Bug: https://bugs.gentoo.org/709746
5 Signed-off-by: Zac Medico <zmedico@g.o>
6 ---
7 lib/_emerge/AbstractEbuildProcess.py | 8 +++-
8 lib/_emerge/SpawnProcess.py | 43 ++++++++++++++++++-
9 .../ebuild/_config/special_env_vars.py | 2 +-
10 man/make.conf.5 | 3 ++
11 4 files changed, 52 insertions(+), 4 deletions(-)
12
13 diff --git a/lib/_emerge/AbstractEbuildProcess.py b/lib/_emerge/AbstractEbuildProcess.py
14 index ddf04e9b3..96801f200 100644
15 --- a/lib/_emerge/AbstractEbuildProcess.py
16 +++ b/lib/_emerge/AbstractEbuildProcess.py
17 @@ -20,7 +20,7 @@ from portage.package.ebuild._ipc.QueryCommand import QueryCommand
18 from portage import shutil, os
19 from portage.util.futures import asyncio
20 from portage.util._pty import _create_pty_or_pipe
21 -from portage.util import apply_secpass_permissions
22 +from portage.util import apply_secpass_permissions, shlex_split
23
24 portage.proxy.lazyimport.lazyimport(globals(),
25 'portage.package.ebuild.doebuild:_global_pid_phases',
26 @@ -196,6 +196,12 @@ class AbstractEbuildProcess(SpawnProcess):
27 null_fd = os.open('/dev/null', os.O_RDONLY)
28 self.fd_pipes[0] = null_fd
29
30 + log_filter_command = self.settings.get('PORTAGE_LOG_FILTER_COMMAND')
31 + if log_filter_command is not None:
32 + log_filter_command = shlex_split(log_filter_command)
33 + if log_filter_command:
34 + self.log_filter_command = log_filter_command
35 +
36 try:
37 SpawnProcess._start(self)
38 finally:
39 diff --git a/lib/_emerge/SpawnProcess.py b/lib/_emerge/SpawnProcess.py
40 index 395d66bb9..f439a5241 100644
41 --- a/lib/_emerge/SpawnProcess.py
42 +++ b/lib/_emerge/SpawnProcess.py
43 @@ -34,8 +34,8 @@ class SpawnProcess(SubProcess):
44 "path_lookup", "pre_exec", "close_fds", "cgroup",
45 "unshare_ipc", "unshare_mount", "unshare_pid", "unshare_net")
46
47 - __slots__ = ("args",) + \
48 - _spawn_kwarg_names + ("_pipe_logger", "_selinux_type",)
49 + __slots__ = ("args", "log_filter_command") + \
50 + _spawn_kwarg_names + ("_filter_proc", "_pipe_logger", "_selinux_type",)
51
52 # Max number of attempts to kill the processes listed in cgroup.procs,
53 # given that processes may fork before they can be killed.
54 @@ -137,6 +137,32 @@ class SpawnProcess(SubProcess):
55 fcntl.fcntl(stdout_fd,
56 fcntl.F_GETFD) | fcntl.FD_CLOEXEC)
57
58 + if self.log_filter_command is not None:
59 + pr, pw = os.pipe()
60 + # stderr goes to /dev/null because the program is expected
61 + # to generate an EIO error when master_fd reaches EOF.
62 + with open(os.devnull, 'wb', 0) as null_fd:
63 + filter_fd_pipes = {0: master_fd, 1: pw, 2: null_fd.fileno()}
64 + self._filter_proc = SpawnProcess(
65 + args=self.log_filter_command,
66 + env=self.env,
67 + fd_pipes=filter_fd_pipes,
68 + scheduler=self.scheduler)
69 + self._filter_proc.addExitListener(self._filter_proc_exit)
70 + try:
71 + self._filter_proc.start()
72 + except portage.exception.CommandNotFound:
73 + self._filter_proc.removeExitListener(self._filter_proc_exit)
74 + self._filter_proc._unregister()
75 + self._filter_proc = None
76 + os.close(pw)
77 + os.close(pr)
78 + else:
79 + os.close(pw)
80 + os.close(master_fd)
81 + # Send self._filter_proc output to PipeLogger
82 + master_fd = pr
83 +
84 self._pipe_logger = PipeLogger(background=self.background,
85 scheduler=self.scheduler, input_fd=master_fd,
86 log_file_path=log_file_path,
87 @@ -171,11 +197,24 @@ class SpawnProcess(SubProcess):
88 self._pipe_logger = None
89 self._async_waitpid()
90
91 + def _filter_proc_exit(self, filter_proc):
92 + self._filter_proc = None
93 +
94 + def _async_waitpid(self):
95 + if self._filter_proc is not None:
96 + # All output should have been collected by now, so kill it.
97 + self._filter_proc.cancel()
98 + SubProcess._async_waitpid(self)
99 +
100 def _unregister(self):
101 SubProcess._unregister(self)
102 if self.cgroup is not None:
103 self._cgroup_cleanup()
104 self.cgroup = None
105 + if self._filter_proc is not None:
106 + self._filter_proc.removeExitListener(self._filter_proc_exit)
107 + self._filter_proc.cancel()
108 + self._filter_proc = None
109 if self._pipe_logger is not None:
110 self._pipe_logger.cancel()
111 self._pipe_logger = None
112 diff --git a/lib/portage/package/ebuild/_config/special_env_vars.py b/lib/portage/package/ebuild/_config/special_env_vars.py
113 index dc01339f7..4f9430e58 100644
114 --- a/lib/portage/package/ebuild/_config/special_env_vars.py
115 +++ b/lib/portage/package/ebuild/_config/special_env_vars.py
116 @@ -175,7 +175,7 @@ environ_filter += [
117 "PORTAGE_RO_DISTDIRS",
118 "PORTAGE_RSYNC_EXTRA_OPTS", "PORTAGE_RSYNC_OPTS",
119 "PORTAGE_RSYNC_RETRIES", "PORTAGE_SSH_OPTS", "PORTAGE_SYNC_STALE",
120 - "PORTAGE_USE",
121 + "PORTAGE_USE", "PORTAGE_LOG_FILTER_COMMAND",
122 "PORTAGE_LOGDIR", "PORTAGE_LOGDIR_CLEAN",
123 "QUICKPKG_DEFAULT_OPTS", "REPOMAN_DEFAULT_OPTS",
124 "RESUMECOMMAND", "RESUMECOMMAND_FTP",
125 diff --git a/man/make.conf.5 b/man/make.conf.5
126 index f82fed65a..04ac75de4 100644
127 --- a/man/make.conf.5
128 +++ b/man/make.conf.5
129 @@ -979,6 +979,9 @@ with an integer pid. For example, a value of "ionice \-c 3 \-p \\${PID}"
130 will set idle io priority. For more information about ionice, see
131 \fBionice\fR(1). This variable is unset by default.
132 .TP
133 +.B PORTAGE_LOG_FILTER_COMMAND
134 +This variable specifies a command that filters build log content.
135 +.TP
136 .B PORTAGE_LOGDIR
137 This variable defines the directory in which per\-ebuild logs are kept.
138 Logs are created only when this is set. They are stored as
139 --
140 2.24.1