1 |
This variable specifies a command that filters build log output to a |
2 |
log file. The plan is to extend this to support a separate filter for |
3 |
tty output in the future. |
4 |
|
5 |
In order to enable the EbuildPhase class to write elog messages to |
6 |
the build log with PORTAGE_LOG_FILTER_FILE_CMD support, convert its |
7 |
_elog method to a coroutine, and add a SchedulerInterface async_output |
8 |
method for it to use. |
9 |
|
10 |
Use a new BuildLogger class to manage log output (with or without a |
11 |
filter command), with compression support provided by PipeLogger. |
12 |
BuildLogger has a stdin property which provides access to a writable |
13 |
binary file stream (refers to a pipe) that log content is written to. |
14 |
|
15 |
Bug: https://bugs.gentoo.org/709746 |
16 |
Signed-off-by: Zac Medico <zmedico@g.o> |
17 |
--- |
18 |
lib/_emerge/AbstractEbuildProcess.py | 3 +- |
19 |
lib/_emerge/BinpkgFetcher.py | 3 +- |
20 |
lib/_emerge/EbuildFetcher.py | 3 +- |
21 |
lib/_emerge/EbuildPhase.py | 47 ++++++-- |
22 |
lib/_emerge/SpawnProcess.py | 58 +++++++--- |
23 |
lib/portage/dbapi/_MergeProcess.py | 3 +- |
24 |
.../ebuild/_config/special_env_vars.py | 8 +- |
25 |
lib/portage/util/_async/BuildLogger.py | 109 ++++++++++++++++++ |
26 |
lib/portage/util/_async/SchedulerInterface.py | 32 ++++- |
27 |
man/make.conf.5 | 7 +- |
28 |
10 files changed, 243 insertions(+), 30 deletions(-) |
29 |
create mode 100644 lib/portage/util/_async/BuildLogger.py |
30 |
|
31 |
diff --git a/lib/_emerge/AbstractEbuildProcess.py b/lib/_emerge/AbstractEbuildProcess.py |
32 |
index 1c1955cfe..ae1aae55f 100644 |
33 |
--- a/lib/_emerge/AbstractEbuildProcess.py |
34 |
+++ b/lib/_emerge/AbstractEbuildProcess.py |
35 |
@@ -1,4 +1,4 @@ |
36 |
-# Copyright 1999-2019 Gentoo Foundation |
37 |
+# Copyright 1999-2020 Gentoo Authors |
38 |
# Distributed under the terms of the GNU General Public License v2 |
39 |
|
40 |
import errno |
41 |
@@ -196,6 +196,7 @@ class AbstractEbuildProcess(SpawnProcess): |
42 |
null_fd = os.open('/dev/null', os.O_RDONLY) |
43 |
self.fd_pipes[0] = null_fd |
44 |
|
45 |
+ self.log_filter_file = self.settings.get('PORTAGE_LOG_FILTER_FILE_CMD') |
46 |
try: |
47 |
SpawnProcess._start(self) |
48 |
finally: |
49 |
diff --git a/lib/_emerge/BinpkgFetcher.py b/lib/_emerge/BinpkgFetcher.py |
50 |
index 36d027de3..2e5861cc1 100644 |
51 |
--- a/lib/_emerge/BinpkgFetcher.py |
52 |
+++ b/lib/_emerge/BinpkgFetcher.py |
53 |
@@ -1,4 +1,4 @@ |
54 |
-# Copyright 1999-2018 Gentoo Foundation |
55 |
+# Copyright 1999-2020 Gentoo Authors |
56 |
# Distributed under the terms of the GNU General Public License v2 |
57 |
|
58 |
import functools |
59 |
@@ -158,6 +158,7 @@ class _BinpkgFetcherProcess(SpawnProcess): |
60 |
self.env = fetch_env |
61 |
if settings.selinux_enabled(): |
62 |
self._selinux_type = settings["PORTAGE_FETCH_T"] |
63 |
+ self.log_filter_file = settings.get('PORTAGE_LOG_FILTER_FILE_CMD') |
64 |
SpawnProcess._start(self) |
65 |
|
66 |
def _pipe(self, fd_pipes): |
67 |
diff --git a/lib/_emerge/EbuildFetcher.py b/lib/_emerge/EbuildFetcher.py |
68 |
index 1e40994fb..55349c33c 100644 |
69 |
--- a/lib/_emerge/EbuildFetcher.py |
70 |
+++ b/lib/_emerge/EbuildFetcher.py |
71 |
@@ -1,4 +1,4 @@ |
72 |
-# Copyright 1999-2018 Gentoo Foundation |
73 |
+# Copyright 1999-2020 Gentoo Authors |
74 |
# Distributed under the terms of the GNU General Public License v2 |
75 |
|
76 |
import copy |
77 |
@@ -225,6 +225,7 @@ class _EbuildFetcherProcess(ForkProcess): |
78 |
settings["NOCOLOR"] = nocolor |
79 |
|
80 |
self._settings = settings |
81 |
+ self.log_filter_file = settings.get('PORTAGE_LOG_FILTER_FILE_CMD') |
82 |
ForkProcess._start(self) |
83 |
|
84 |
# Free settings now since it's no longer needed in |
85 |
diff --git a/lib/_emerge/EbuildPhase.py b/lib/_emerge/EbuildPhase.py |
86 |
index 477e0ba97..ddb3dc719 100644 |
87 |
--- a/lib/_emerge/EbuildPhase.py |
88 |
+++ b/lib/_emerge/EbuildPhase.py |
89 |
@@ -26,6 +26,8 @@ from portage.package.ebuild.prepare_build_dirs import (_prepare_workdir, |
90 |
from portage.util.futures.compat_coroutine import coroutine |
91 |
from portage.util import writemsg |
92 |
from portage.util._async.AsyncTaskFuture import AsyncTaskFuture |
93 |
+from portage.util._async.BuildLogger import BuildLogger |
94 |
+from portage.util.futures import asyncio |
95 |
from portage.util.futures.executor.fork import ForkExecutor |
96 |
|
97 |
try: |
98 |
@@ -69,6 +71,11 @@ class EbuildPhase(CompositeTask): |
99 |
_locked_phases = ("setup", "preinst", "postinst", "prerm", "postrm") |
100 |
|
101 |
def _start(self): |
102 |
+ future = asyncio.ensure_future(self._async_start(), loop=self.scheduler) |
103 |
+ self._start_task(AsyncTaskFuture(future=future), self._async_start_exit) |
104 |
+ |
105 |
+ @coroutine |
106 |
+ def _async_start(self): |
107 |
|
108 |
need_builddir = self.phase not in EbuildProcess._phases_without_builddir |
109 |
|
110 |
@@ -126,7 +133,7 @@ class EbuildPhase(CompositeTask): |
111 |
# Force background=True for this header since it's intended |
112 |
# for the log and it doesn't necessarily need to be visible |
113 |
# elsewhere. |
114 |
- self._elog('einfo', msg, background=True) |
115 |
+ yield self._elog('einfo', msg, background=True) |
116 |
|
117 |
if self.phase == 'package': |
118 |
if 'PORTAGE_BINPKG_TMPFILE' not in self.settings: |
119 |
@@ -134,6 +141,12 @@ class EbuildPhase(CompositeTask): |
120 |
os.path.join(self.settings['PKGDIR'], |
121 |
self.settings['CATEGORY'], self.settings['PF']) + '.tbz2' |
122 |
|
123 |
+ def _async_start_exit(self, task): |
124 |
+ task.future.cancelled() or task.future.result() |
125 |
+ if self._default_exit(task) != os.EX_OK: |
126 |
+ self.wait() |
127 |
+ return |
128 |
+ |
129 |
if self.phase in ("pretend", "prerm"): |
130 |
env_extractor = BinpkgEnvExtractor(background=self.background, |
131 |
scheduler=self.scheduler, settings=self.settings) |
132 |
@@ -391,6 +404,7 @@ class EbuildPhase(CompositeTask): |
133 |
self.returncode = 1 |
134 |
self.wait() |
135 |
|
136 |
+ @coroutine |
137 |
def _elog(self, elog_funcname, lines, background=None): |
138 |
if background is None: |
139 |
background = self.background |
140 |
@@ -407,11 +421,30 @@ class EbuildPhase(CompositeTask): |
141 |
portage.output.havecolor = global_havecolor |
142 |
msg = out.getvalue() |
143 |
if msg: |
144 |
- log_path = None |
145 |
- if self.settings.get("PORTAGE_BACKGROUND") != "subprocess": |
146 |
- log_path = self.settings.get("PORTAGE_LOG_FILE") |
147 |
- self.scheduler.output(msg, log_path=log_path, |
148 |
- background=background) |
149 |
+ build_logger = None |
150 |
+ try: |
151 |
+ log_file = None |
152 |
+ log_path = None |
153 |
+ if self.settings.get("PORTAGE_BACKGROUND") != "subprocess": |
154 |
+ log_path = self.settings.get("PORTAGE_LOG_FILE") |
155 |
+ if log_path: |
156 |
+ build_logger = BuildLogger(env=self.settings.environ(), |
157 |
+ log_path=log_path, |
158 |
+ log_filter_file=self.settings.get('PORTAGE_LOG_FILTER_FILE_CMD'), |
159 |
+ scheduler=self.scheduler) |
160 |
+ build_logger.start() |
161 |
+ log_file = build_logger.stdin |
162 |
+ |
163 |
+ yield self.scheduler.async_output(msg, log_file=log_file, |
164 |
+ background=background) |
165 |
+ |
166 |
+ if build_logger is not None: |
167 |
+ build_logger.stdin.close() |
168 |
+ yield build_logger.async_wait() |
169 |
+ except asyncio.CancelledError: |
170 |
+ if build_logger is not None: |
171 |
+ build_logger.cancel() |
172 |
+ raise |
173 |
|
174 |
|
175 |
class _PostPhaseCommands(CompositeTask): |
176 |
@@ -480,4 +513,4 @@ class _PostPhaseCommands(CompositeTask): |
177 |
qa_msg.extend("\t%s: %s" % (filename, " ".join(sorted(soname_deps))) |
178 |
for filename, soname_deps in unresolved) |
179 |
qa_msg.append("") |
180 |
- self.elog("eqawarn", qa_msg) |
181 |
+ yield self.elog("eqawarn", qa_msg) |
182 |
diff --git a/lib/_emerge/SpawnProcess.py b/lib/_emerge/SpawnProcess.py |
183 |
index 395d66bb9..f96911571 100644 |
184 |
--- a/lib/_emerge/SpawnProcess.py |
185 |
+++ b/lib/_emerge/SpawnProcess.py |
186 |
@@ -1,4 +1,4 @@ |
187 |
-# Copyright 2008-2018 Gentoo Foundation |
188 |
+# Copyright 2008-2020 Gentoo Authors |
189 |
# Distributed under the terms of the GNU General Public License v2 |
190 |
|
191 |
try: |
192 |
@@ -19,7 +19,10 @@ from portage.const import BASH_BINARY |
193 |
from portage.localization import _ |
194 |
from portage.output import EOutput |
195 |
from portage.util import writemsg_level |
196 |
+from portage.util._async.BuildLogger import BuildLogger |
197 |
from portage.util._async.PipeLogger import PipeLogger |
198 |
+from portage.util.futures import asyncio |
199 |
+from portage.util.futures.compat_coroutine import coroutine |
200 |
|
201 |
class SpawnProcess(SubProcess): |
202 |
|
203 |
@@ -34,8 +37,8 @@ class SpawnProcess(SubProcess): |
204 |
"path_lookup", "pre_exec", "close_fds", "cgroup", |
205 |
"unshare_ipc", "unshare_mount", "unshare_pid", "unshare_net") |
206 |
|
207 |
- __slots__ = ("args",) + \ |
208 |
- _spawn_kwarg_names + ("_pipe_logger", "_selinux_type",) |
209 |
+ __slots__ = ("args", "log_filter_file") + \ |
210 |
+ _spawn_kwarg_names + ("_main_task", "_selinux_type",) |
211 |
|
212 |
# Max number of attempts to kill the processes listed in cgroup.procs, |
213 |
# given that processes may fork before they can be killed. |
214 |
@@ -137,13 +140,43 @@ class SpawnProcess(SubProcess): |
215 |
fcntl.fcntl(stdout_fd, |
216 |
fcntl.F_GETFD) | fcntl.FD_CLOEXEC) |
217 |
|
218 |
- self._pipe_logger = PipeLogger(background=self.background, |
219 |
+ build_logger = BuildLogger(env=self.env, |
220 |
+ log_path=log_file_path, |
221 |
+ log_filter_file=self.log_filter_file, |
222 |
+ scheduler=self.scheduler) |
223 |
+ build_logger.start() |
224 |
+ |
225 |
+ pipe_logger = PipeLogger(background=self.background, |
226 |
scheduler=self.scheduler, input_fd=master_fd, |
227 |
- log_file_path=log_file_path, |
228 |
+ log_file_path=build_logger.stdin, |
229 |
stdout_fd=stdout_fd) |
230 |
- self._pipe_logger.addExitListener(self._pipe_logger_exit) |
231 |
- self._pipe_logger.start() |
232 |
+ |
233 |
+ pipe_logger.start() |
234 |
+ |
235 |
self._registered = True |
236 |
+ self._main_task = asyncio.ensure_future(self._main(build_logger, pipe_logger), loop=self.scheduler) |
237 |
+ self._main_task.add_done_callback(self._main_exit) |
238 |
+ |
239 |
+ @coroutine |
240 |
+ def _main(self, build_logger, pipe_logger): |
241 |
+ try: |
242 |
+ if pipe_logger.poll() is None: |
243 |
+ yield pipe_logger.async_wait() |
244 |
+ if build_logger.poll() is None: |
245 |
+ yield build_logger.async_wait() |
246 |
+ except asyncio.CancelledError: |
247 |
+ if pipe_logger.poll() is None: |
248 |
+ pipe_logger.cancel() |
249 |
+ if build_logger.poll() is None: |
250 |
+ build_logger.cancel() |
251 |
+ raise |
252 |
+ |
253 |
+ def _main_exit(self, main_task): |
254 |
+ try: |
255 |
+ main_task.result() |
256 |
+ except asyncio.CancelledError: |
257 |
+ self.cancel() |
258 |
+ self._async_waitpid() |
259 |
|
260 |
def _can_log(self, slave_fd): |
261 |
return True |
262 |
@@ -167,20 +200,17 @@ class SpawnProcess(SubProcess): |
263 |
|
264 |
return spawn_func(args, **kwargs) |
265 |
|
266 |
- def _pipe_logger_exit(self, pipe_logger): |
267 |
- self._pipe_logger = None |
268 |
- self._async_waitpid() |
269 |
- |
270 |
def _unregister(self): |
271 |
SubProcess._unregister(self) |
272 |
if self.cgroup is not None: |
273 |
self._cgroup_cleanup() |
274 |
self.cgroup = None |
275 |
- if self._pipe_logger is not None: |
276 |
- self._pipe_logger.cancel() |
277 |
- self._pipe_logger = None |
278 |
+ if self._main_task is not None: |
279 |
+ self._main_task.done() or self._main_task.cancel() |
280 |
|
281 |
def _cancel(self): |
282 |
+ if self._main_task is not None: |
283 |
+ self._main_task.done() or self._main_task.cancel() |
284 |
SubProcess._cancel(self) |
285 |
self._cgroup_cleanup() |
286 |
|
287 |
diff --git a/lib/portage/dbapi/_MergeProcess.py b/lib/portage/dbapi/_MergeProcess.py |
288 |
index 371550079..236d1a255 100644 |
289 |
--- a/lib/portage/dbapi/_MergeProcess.py |
290 |
+++ b/lib/portage/dbapi/_MergeProcess.py |
291 |
@@ -1,4 +1,4 @@ |
292 |
-# Copyright 2010-2018 Gentoo Foundation |
293 |
+# Copyright 2010-2020 Gentoo Authors |
294 |
# Distributed under the terms of the GNU General Public License v2 |
295 |
|
296 |
import io |
297 |
@@ -57,6 +57,7 @@ class MergeProcess(ForkProcess): |
298 |
self.fd_pipes = self.fd_pipes.copy() |
299 |
self.fd_pipes.setdefault(0, portage._get_stdin().fileno()) |
300 |
|
301 |
+ self.log_filter_file = self.settings.get('PORTAGE_LOG_FILTER_FILE_CMD') |
302 |
super(MergeProcess, self)._start() |
303 |
|
304 |
def _lock_vdb(self): |
305 |
diff --git a/lib/portage/package/ebuild/_config/special_env_vars.py b/lib/portage/package/ebuild/_config/special_env_vars.py |
306 |
index 440dd00b2..f44cb9b1b 100644 |
307 |
--- a/lib/portage/package/ebuild/_config/special_env_vars.py |
308 |
+++ b/lib/portage/package/ebuild/_config/special_env_vars.py |
309 |
@@ -1,4 +1,4 @@ |
310 |
-# Copyright 2010-2019 Gentoo Authors |
311 |
+# Copyright 2010-2020 Gentoo Authors |
312 |
# Distributed under the terms of the GNU General Public License v2 |
313 |
|
314 |
from __future__ import unicode_literals |
315 |
@@ -175,7 +175,7 @@ environ_filter += [ |
316 |
"PORTAGE_RO_DISTDIRS", |
317 |
"PORTAGE_RSYNC_EXTRA_OPTS", "PORTAGE_RSYNC_OPTS", |
318 |
"PORTAGE_RSYNC_RETRIES", "PORTAGE_SSH_OPTS", "PORTAGE_SYNC_STALE", |
319 |
- "PORTAGE_USE", |
320 |
+ "PORTAGE_USE", "PORTAGE_LOG_FILTER_FILE_CMD", |
321 |
"PORTAGE_LOGDIR", "PORTAGE_LOGDIR_CLEAN", |
322 |
"QUICKPKG_DEFAULT_OPTS", "REPOMAN_DEFAULT_OPTS", |
323 |
"RESUMECOMMAND", "RESUMECOMMAND_FTP", |
324 |
@@ -204,7 +204,9 @@ default_globals = { |
325 |
'PORTAGE_BZIP2_COMMAND': 'bzip2', |
326 |
} |
327 |
|
328 |
-validate_commands = ('PORTAGE_BZIP2_COMMAND', 'PORTAGE_BUNZIP2_COMMAND',) |
329 |
+validate_commands = ('PORTAGE_BZIP2_COMMAND', 'PORTAGE_BUNZIP2_COMMAND', |
330 |
+ 'PORTAGE_LOG_FILTER_FILE_CMD', |
331 |
+) |
332 |
|
333 |
# To enhance usability, make some vars case insensitive |
334 |
# by forcing them to lower case. |
335 |
diff --git a/lib/portage/util/_async/BuildLogger.py b/lib/portage/util/_async/BuildLogger.py |
336 |
new file mode 100644 |
337 |
index 000000000..f5fea77ea |
338 |
--- /dev/null |
339 |
+++ b/lib/portage/util/_async/BuildLogger.py |
340 |
@@ -0,0 +1,109 @@ |
341 |
+# Copyright 2020 Gentoo Authors |
342 |
+# Distributed under the terms of the GNU General Public License v2 |
343 |
+ |
344 |
+import subprocess |
345 |
+ |
346 |
+from portage import os |
347 |
+from portage.util import shlex_split |
348 |
+from _emerge.AsynchronousTask import AsynchronousTask |
349 |
+from portage.util._async.PipeLogger import PipeLogger |
350 |
+from portage.util._async.PopenProcess import PopenProcess |
351 |
+from portage.util.futures import asyncio |
352 |
+from portage.util.futures.compat_coroutine import coroutine |
353 |
+ |
354 |
+ |
355 |
+class BuildLogger(AsynchronousTask): |
356 |
+ """ |
357 |
+ Write to a log file, with compression support provided by PipeLogger. |
358 |
+ If the log_filter_file parameter is specified, then it is interpreted |
359 |
+ as a command to execute which filters log output (see the |
360 |
+ PORTAGE_LOG_FILTER_FILE_CMD variable in make.conf(5)). The stdin property |
361 |
+ provides access to a writable binary file stream (refers to a pipe) |
362 |
+ that log content should be written to (usually redirected from |
363 |
+ subprocess stdout and stderr streams). |
364 |
+ """ |
365 |
+ |
366 |
+ __slots__ = ('env', 'log_path', 'log_filter_file', '_main_task', '_stdin') |
367 |
+ |
368 |
+ @property |
369 |
+ def stdin(self): |
370 |
+ return self._stdin |
371 |
+ |
372 |
+ def _start(self): |
373 |
+ filter_proc = None |
374 |
+ log_input = None |
375 |
+ if self.log_path is not None: |
376 |
+ log_filter_file = self.log_filter_file |
377 |
+ if log_filter_file is not None: |
378 |
+ split_value = shlex_split(log_filter_file) |
379 |
+ log_filter_file = split_value if split_value else None |
380 |
+ if log_filter_file: |
381 |
+ filter_input, stdin = os.pipe() |
382 |
+ log_input, filter_output = os.pipe() |
383 |
+ try: |
384 |
+ filter_proc = PopenProcess( |
385 |
+ proc=subprocess.Popen( |
386 |
+ log_filter_file, |
387 |
+ env=self.env, |
388 |
+ stdin=filter_input, |
389 |
+ stdout=filter_output, |
390 |
+ stderr=filter_output, |
391 |
+ ), |
392 |
+ scheduler=self.scheduler, |
393 |
+ ) |
394 |
+ filter_proc.start() |
395 |
+ except EnvironmentError: |
396 |
+ # Maybe the command is missing or broken somehow... |
397 |
+ os.close(filter_input) |
398 |
+ os.close(stdin) |
399 |
+ os.close(log_input) |
400 |
+ os.close(filter_output) |
401 |
+ else: |
402 |
+ self._stdin = os.fdopen(stdin, 'wb', 0) |
403 |
+ os.close(filter_input) |
404 |
+ os.close(filter_output) |
405 |
+ |
406 |
+ if self._stdin is None: |
407 |
+ # Since log_filter_file is unspecified or refers to a file |
408 |
+ # that is missing or broken somehow, create a pipe that |
409 |
+ # logs directly to pipe_logger. |
410 |
+ log_input, stdin = os.pipe() |
411 |
+ self._stdin = os.fdopen(stdin, 'wb', 0) |
412 |
+ |
413 |
+ # Set background=True so that pipe_logger does not log to stdout. |
414 |
+ pipe_logger = PipeLogger(background=True, |
415 |
+ scheduler=self.scheduler, input_fd=log_input, |
416 |
+ log_file_path=self.log_path) |
417 |
+ pipe_logger.start() |
418 |
+ |
419 |
+ self._main_task = asyncio.ensure_future(self._main(filter_proc, pipe_logger), loop=self.scheduler) |
420 |
+ self._main_task.add_done_callback(self._main_exit) |
421 |
+ |
422 |
+ @coroutine |
423 |
+ def _main(self, filter_proc, pipe_logger): |
424 |
+ try: |
425 |
+ if pipe_logger.poll() is None: |
426 |
+ yield pipe_logger.async_wait() |
427 |
+ if filter_proc is not None and filter_proc.poll() is None: |
428 |
+ yield filter_proc.async_wait() |
429 |
+ except asyncio.CancelledError: |
430 |
+ if pipe_logger.poll() is None: |
431 |
+ pipe_logger.cancel() |
432 |
+ if filter_proc is not None and filter_proc.poll() is None: |
433 |
+ filter_proc.cancel() |
434 |
+ raise |
435 |
+ |
436 |
+ def _cancel(self): |
437 |
+ if self._main_task is not None: |
438 |
+ self._main_task.done() or self._main_task.cancel() |
439 |
+ if self._stdin is not None and not self._stdin.closed: |
440 |
+ self._stdin.close() |
441 |
+ |
442 |
+ def _main_exit(self, main_task): |
443 |
+ try: |
444 |
+ main_task.result() |
445 |
+ except asyncio.CancelledError: |
446 |
+ self.cancel() |
447 |
+ self._was_cancelled() |
448 |
+ self.returncode = self.returncode or 0 |
449 |
+ self._async_wait() |
450 |
diff --git a/lib/portage/util/_async/SchedulerInterface.py b/lib/portage/util/_async/SchedulerInterface.py |
451 |
index ec6417da1..3ff250d1d 100644 |
452 |
--- a/lib/portage/util/_async/SchedulerInterface.py |
453 |
+++ b/lib/portage/util/_async/SchedulerInterface.py |
454 |
@@ -1,4 +1,4 @@ |
455 |
-# Copyright 2012-2018 Gentoo Foundation |
456 |
+# Copyright 2012-2020 Gentoo Authors |
457 |
# Distributed under the terms of the GNU General Public License v2 |
458 |
|
459 |
import gzip |
460 |
@@ -7,6 +7,8 @@ import errno |
461 |
from portage import _encodings |
462 |
from portage import _unicode_encode |
463 |
from portage.util import writemsg_level |
464 |
+from portage.util.futures._asyncio.streams import _writer |
465 |
+from portage.util.futures.compat_coroutine import coroutine |
466 |
from ..SlotObject import SlotObject |
467 |
|
468 |
class SchedulerInterface(SlotObject): |
469 |
@@ -53,6 +55,34 @@ class SchedulerInterface(SlotObject): |
470 |
def _return_false(): |
471 |
return False |
472 |
|
473 |
+ @coroutine |
474 |
+ def async_output(self, msg, log_file=None, background=None, |
475 |
+ level=0, noiselevel=-1): |
476 |
+ """ |
477 |
+ Output a msg to stdio (if not in background) and to a log file |
478 |
+ if provided. |
479 |
+ |
480 |
+ @param msg: a message string, including newline if appropriate |
481 |
+ @type msg: str |
482 |
+ @param log_file: log file in binary mode |
483 |
+ @type log_file: file |
484 |
+ @param background: send messages only to log (not to stdio) |
485 |
+ @type background: bool |
486 |
+ @param level: a numeric logging level (see the logging module) |
487 |
+ @type level: int |
488 |
+ @param noiselevel: passed directly to writemsg |
489 |
+ @type noiselevel: int |
490 |
+ """ |
491 |
+ global_background = self._is_background() |
492 |
+ if background is None or global_background: |
493 |
+ background = global_background |
494 |
+ |
495 |
+ if not background: |
496 |
+ writemsg_level(msg, level=level, noiselevel=noiselevel) |
497 |
+ |
498 |
+ if log_file is not None: |
499 |
+ yield _writer(log_file, _unicode_encode(msg)) |
500 |
+ |
501 |
def output(self, msg, log_path=None, background=None, |
502 |
level=0, noiselevel=-1): |
503 |
""" |
504 |
diff --git a/man/make.conf.5 b/man/make.conf.5 |
505 |
index a3bd662ae..eb812150f 100644 |
506 |
--- a/man/make.conf.5 |
507 |
+++ b/man/make.conf.5 |
508 |
@@ -1,4 +1,4 @@ |
509 |
-.TH "MAKE.CONF" "5" "May 2020" "Portage VERSION" "Portage" |
510 |
+.TH "MAKE.CONF" "5" "Jun 2020" "Portage VERSION" "Portage" |
511 |
.SH "NAME" |
512 |
make.conf \- custom settings for Portage |
513 |
.SH "SYNOPSIS" |
514 |
@@ -979,6 +979,11 @@ with an integer pid. For example, a value of "ionice \-c 3 \-p \\${PID}" |
515 |
will set idle io priority. For more information about ionice, see |
516 |
\fBionice\fR(1). This variable is unset by default. |
517 |
.TP |
518 |
+.B PORTAGE_LOG_FILTER_FILE_CMD |
519 |
+This variable specifies a command that filters build log output to a |
520 |
+log file. In order to filter ANSI escape codes from build logs, |
521 |
+\fBansifilter\fR(1) is a convenient setting for this variable. |
522 |
+.TP |
523 |
.B PORTAGE_LOGDIR |
524 |
This variable defines the directory in which per\-ebuild logs are kept. |
525 |
Logs are created only when this is set. They are stored as |
526 |
-- |
527 |
2.25.3 |