Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 155161 | Differences between
and this patch

Collapse All | Expand All

(-)a/bin/ebuild.sh (-1 / +142 lines)
Lines 130-136 __qa_source() { Link Here
130
__qa_call() {
130
__qa_call() {
131
	local shopts=$(shopt) OLDIFS="$IFS"
131
	local shopts=$(shopt) OLDIFS="$IFS"
132
	local retval
132
	local retval
133
	"$@"
133
	__call-ebuildshell "$@"
134
	retval=$?
134
	retval=$?
135
	set +e
135
	set +e
136
	[[ $shopts != $(shopt) ]] &&
136
	[[ $shopts != $(shopt) ]] &&
Lines 537-542 if [[ -n ${QA_INTERCEPTORS} ]] ; then Link Here
537
	unset BIN_PATH BIN BODY FUNC_SRC
537
	unset BIN_PATH BIN BODY FUNC_SRC
538
fi
538
fi
539
539
540
__call-ebuildshell() {
541
	if ! has ebuildshell ${FEATURES}; then
542
		"$@"
543
		return $?
544
	fi
545
	local __ebuildshell_args=( "$@" )
546
	# These are the variables I have seen 'bash -i' maintaining the values for:
547
	local __ebuildshell_bash_i_vars="__ebuildshell_.*
548
		_ BASH_ARGC BASH_ARGV BASH_COMMAND BASH_LINENO BASH_SOURCE
549
		BASH_VERSINFO BASH_SUBSHELL BASHOPTS BASHPID COMP_WORDBREAKS
550
		DIRSTACK EUID FUNCNAME GROUPS HISTCMD HISTFILE LINENO PIPESTATUS
551
		PPID PS1 PS2 PS3 PS4 PWD RANDOM SECONDS SHELLOPTS UID"
552
	# Allow recursive ebuildshell, for use in multibuild.eclass and similar:
553
	local __ebuildshell_pid=${BASHPID:-$(__bashpid)}
554
	local __ebuildshell_tmpf="${T}/ebuildshell.${__ebuildshell_pid}"
555
	rm -f "${__ebuildshell_tmpf}."{ebuild,return}-{env,rovars}
556
	(
557
		cat <<-EOE
558
			# local variables of functions using recursive ebuildshell are
559
			# visible to the EXIT trap of that recursive ebuildshell.  To
560
			# keep them local, we have to filter them from that recursive
561
			# ebuildshell's return-env.  As 'declare -p' is unable to tell
562
			# local-ity of variables, we abuse the trace attribute for local
563
			# variables to filter them from the return-env.  So we need the
564
			# local alias active before declaring any functions.
565
			# On a sidehand, this allows for copy&paste of function body
566
			# lines including the local keyword.
567
			alias local='declare -t'
568
			shopt -s expand_aliases
569
		EOE
570
		(
571
			declare -p
572
			declare -fp
573
			shopt -p
574
			[[ ${BASH_VERSINFO[0]} == 3 ]] && export
575
		) |
576
		(
577
			# we need everything but the bash vars after 'env -i'
578
			2>"${__ebuildshell_tmpf}.ebuild-rovars" \
579
			"${PORTAGE_PYTHON:-/tools/haubi/gentoo/s01en24/usr/bin/python}" \
580
				"${PORTAGE_BIN_PATH}"/filter-bash-environment.py \
581
					--report-readonly-variables \
582
					--preserve-readonly-attribute \
583
					"${__ebuildshell_bash_i_vars}" \
584
				|| die "filter-bash-environment.py failed"
585
		)
586
		# 'declare -g' is available since bash-4.2,
587
		# https://bugs.gentoo.org/show_bug.cgi?id=155161#c35
588
		if (( ${BASH_VERSINFO[0]} > 4 )) ||
589
		   (( ${BASH_VERSINFO[0]} == 4 && ${BASH_VERSINFO[1]} >= 2 ))
590
		then
591
			__ebuildshell_bash42_true=
592
			__ebuildshell_bash42_false='#bash-4.2#'
593
		else
594
		    __ebuildshell_bash42_true='#bash-4.2#'
595
		    __ebuildshell_bash42_false=
596
		fi
597
		# The already readonly variables, without bash maintained ones:
598
		__ebuildshell_ro_ebuild_vars=$(<"${__ebuildshell_tmpf}.ebuild-rovars")
599
		cat <<-EOE
600
			# properly quote the function arguments
601
			$(declare -p __ebuildshell_args)
602
			set -- "\${__ebuildshell_args[@]}"
603
			unset __ebuildshell_args
604
			# be informative about what to do
605
			PS1="EBUILD ${PN} $1 \$ "
606
			type $1
607
			${__ebuildshell_bash42_false}echo 'warning: preserving variables across phases requires bash-4.2'
608
			echo "WANTED: \$@"
609
			echo "or use: \"\\\$@\""
610
			# use bash history, but not the 'user's real one
611
			HISTFILE=~/.bash_history
612
			# but do not use history-expansion with '!',
613
			# for copy&paste of function body lines containing: !
614
			set +H
615
			# this is a debugging shell already
616
			shopt -u extdebug
617
			trap - DEBUG
618
			# at exit, dump the current environment
619
			trap "
620
				unalias local
621
				unset -f __call-ebuildshell
622
				rm -f '${__ebuildshell_tmpf}.return-'*
623
				(
624
					(
625
						# declare -p does not tell the -g flag,
626
						# so we add it by aliasing declare.
627
						${__ebuildshell_bash42_true}echo \"alias declare='declare -g'\"
628
						declare -p
629
						${__ebuildshell_bash42_true}echo \"unalias declare\"
630
						declare -fp
631
						shopt -p | grep -v '\\(expand_aliases\\|extdebug\\)$'
632
						$([[ ${BASH_VERSINFO[0]} == 3 ]] && echo export)
633
					) |
634
					(
635
						# We may have more readonly variables now, yet we
636
						# need to filter variables that were readonly before.
637
						# And filter local variables by their trace attribute.
638
						2>'${__ebuildshell_tmpf}.return-rovars' \\
639
						'${PORTAGE_PYTHON:-/tools/haubi/gentoo/s01en24/usr/bin/python}' \\
640
							'${PORTAGE_BIN_PATH}'/filter-bash-environment.py \\
641
								--report-readonly-variables \\
642
								--preserve-readonly-attribute \\
643
								--filter-traced-variables \\
644
								'${__ebuildshell_bash_i_vars} \
645
								 ${__ebuildshell_ro_ebuild_vars}' \\
646
							|| die 'filter-bash-environment.py failed'
647
					)
648
				) > '${__ebuildshell_tmpf}.return-env'
649
				" EXIT
650
			# can do some cleanup right now
651
			rm -f '${__ebuildshell_tmpf}.ebuild-'*
652
		EOE
653
	) > "${__ebuildshell_tmpf}.ebuild-env"
654
655
	# pre-fill the history with "$@"
656
	echo '"$@"' >> ~/.bash_history
657
658
	env -i ${BASH} --rcfile "${__ebuildshell_tmpf}.ebuild-env" -i
659
660
	# The environment- and exit-status handling after leaving the ebuildshell
661
	# prompt is expected to be identical as without the ebuildshell prompt.
662
	local __ebuildshell_status=$?
663
664
	# We might be in a recursive ebuildshell, but do not want
665
	# any aliases being active while sourcing the return-env.
666
	local __ebuildshell_orig_aliases=$(alias)
667
	unalias -a
668
	source "${__ebuildshell_tmpf}.return-env"
669
	unalias -a
670
	eval "${__ebuildshell_orig_aliases}"
671
672
	# Portage does whitelist readonly variables. If an ebuild defines
673
	# more readonly variables, their readonly attribute is removed.
674
	# If we ever want to preserve additional readonly variables across
675
	# phases, their names are in "${__ebuildshell_tmpf}.return-rovars".
676
	rm -f "${__ebuildshell_tmpf}."{ebuild,return}-{env,rovars}
677
678
	return ${__ebuildshell_status}
679
}
680
540
# Subshell/helper die support (must export for the die helper).
681
# Subshell/helper die support (must export for the die helper).
541
export EBUILD_MASTER_PID=${BASHPID:-$(__bashpid)}
682
export EBUILD_MASTER_PID=${BASHPID:-$(__bashpid)}
542
trap 'exit 1' SIGTERM
683
trap 'exit 1' SIGTERM
(-)a/bin/filter-bash-environment.py (-16 / +41 lines)
Lines 14-20 func_end_re = re.compile(r'^\}$') Link Here
14
14
15
var_assign_re = re.compile(r'(^|^declare\s+-\S+\s+|^declare\s+|^export\s+)([^=\s]+)=("|\')?.*$')
15
var_assign_re = re.compile(r'(^|^declare\s+-\S+\s+|^declare\s+|^export\s+)([^=\s]+)=("|\')?.*$')
16
close_quote_re = re.compile(r'(\\"|"|\')\s*$')
16
close_quote_re = re.compile(r'(\\"|"|\')\s*$')
17
readonly_re = re.compile(r'^declare\s+-(\S*)r(\S*)\s+')
17
readonly_re = re.compile(r'^declare\s+-(\S*)r(\S*)\s+([^=\s]+)')
18
trace_re = re.compile(r'^declare\s+-\S*t\S*\s+')
18
# declare without assignment
19
# declare without assignment
19
var_declare_re = re.compile(r'^declare(\s+-\S+)?\s+([^=\s]+)\s*$')
20
var_declare_re = re.compile(r'^declare(\s+-\S+)?\s+([^=\s]+)\s*$')
20
21
Lines 29-35 def have_end_quote(quote, line): Link Here
29
	return close_quote_match is not None and \
30
	return close_quote_match is not None and \
30
		close_quote_match.group(1) == quote
31
		close_quote_match.group(1) == quote
31
32
32
def filter_declare_readonly_opt(line):
33
def filter_declare_readonly_opt(line, options):
33
	readonly_match = readonly_re.match(line)
34
	readonly_match = readonly_re.match(line)
34
	if readonly_match is not None:
35
	if readonly_match is not None:
35
		declare_opts = ''
36
		declare_opts = ''
Lines 37-50 def filter_declare_readonly_opt(line): Link Here
37
			group = readonly_match.group(i)
38
			group = readonly_match.group(i)
38
			if group is not None:
39
			if group is not None:
39
				declare_opts += group
40
				declare_opts += group
41
		var = readonly_match.group(3)
42
		if '--report-readonly-variables' in options:
43
			sys.stderr.write(var + "\n")
44
		if '--preserve-readonly-attribute' in options:
45
			declare_opts += 'r'
40
		if declare_opts:
46
		if declare_opts:
41
			line = 'declare -%s %s' % \
47
			line = 'declare -%s %s%s' % \
42
				(declare_opts, line[readonly_match.end():])
48
				(declare_opts, var, line[readonly_match.end():])
43
		else:
49
		else:
44
			line = 'declare ' + line[readonly_match.end():]
50
			line = 'declare ' + var + line[readonly_match.end():]
45
	return line
51
	return line
46
52
47
def filter_bash_environment(pattern, file_in, file_out):
53
def filter_bash_environment(pattern, file_in, file_out, options):
48
	# Filter out any instances of the \1 character from variable values
54
	# Filter out any instances of the \1 character from variable values
49
	# since this character multiplies each time that the environment
55
	# since this character multiplies each time that the environment
50
	# is saved (strange bash behavior). This can eventually result in
56
	# is saved (strange bash behavior). This can eventually result in
Lines 68-73 def filter_bash_environment(pattern, file_in, file_out): Link Here
68
				quote = var_assign_match.group(3)
74
				quote = var_assign_match.group(3)
69
				filter_this = pattern.match(var_assign_match.group(2)) \
75
				filter_this = pattern.match(var_assign_match.group(2)) \
70
					is not None
76
					is not None
77
				if not filter_this and '--filter-traced-variables' in options:
78
					filter_this = trace_re.match(line) is not None
71
				# Exclude the start quote when searching for the end quote,
79
				# Exclude the start quote when searching for the end quote,
72
				# to ensure that the start quote is not misidentified as the
80
				# to ensure that the start quote is not misidentified as the
73
				# end quote (happens if there is a newline immediately after
81
				# end quote (happens if there is a newline immediately after
Lines 77-83 def filter_bash_environment(pattern, file_in, file_out): Link Here
77
					multi_line_quote = quote
85
					multi_line_quote = quote
78
					multi_line_quote_filter = filter_this
86
					multi_line_quote_filter = filter_this
79
				if not filter_this:
87
				if not filter_this:
80
					line = filter_declare_readonly_opt(line)
88
					line = filter_declare_readonly_opt(line, options)
81
					file_out.write(line.replace("\1", ""))
89
					file_out.write(line.replace("\1", ""))
82
				continue
90
				continue
83
			else:
91
			else:
Lines 86-93 def filter_bash_environment(pattern, file_in, file_out): Link Here
86
					# declare without assignment
94
					# declare without assignment
87
					filter_this = pattern.match(declare_match.group(2)) \
95
					filter_this = pattern.match(declare_match.group(2)) \
88
						is not None
96
						is not None
97
					if not filter_this and '--filter-traced-variables' in options:
98
						filter_this = trace_re.match(line) is not None
89
					if not filter_this:
99
					if not filter_this:
90
						line = filter_declare_readonly_opt(line)
100
						line = filter_declare_readonly_opt(line, options)
91
						file_out.write(line)
101
						file_out.write(line)
92
					continue
102
					continue
93
103
Lines 124-136 if __name__ == "__main__": Link Here
124
		"while leaving bash function definitions and here-documents " + \
134
		"while leaving bash function definitions and here-documents " + \
125
		"intact. The PATTERN is a space separated list of variable names" + \
135
		"intact. The PATTERN is a space separated list of variable names" + \
126
		" and it supports python regular expression syntax."
136
		" and it supports python regular expression syntax."
127
	usage = "usage: %s PATTERN" % os.path.basename(sys.argv[0])
137
	usage = "usage: %s [-h|<options>] PATTERN" % os.path.basename(sys.argv[0])
128
	args = sys.argv[1:]
138
	args = []
129
139
	known_options = {
130
	if '-h' in args or '--help' in args:
140
		'--report-readonly-variables':
131
		sys.stdout.write(usage + "\n")
141
			"Write names of readonly variables to stderr.",
132
		sys.stdout.flush()
142
		'--preserve-readonly-attribute':
133
		sys.exit(os.EX_OK)
143
			"Preserve the '-r' flag in 'declare -r'.",
144
		'--filter-traced-variables':
145
			"Filter out variables declared with '-t' attribute."
146
	}
147
	options = {}
148
	for arg in sys.argv[1:]:
149
		if arg in known_options.keys():
150
			options[arg] = True
151
			continue
152
		if '-h' == arg or '--help' == arg:
153
			sys.stdout.write(usage + "\n\nKnown <options>:\n\n")
154
			for option, descr in known_options.items():
155
				sys.stdout.write("  " + option + "\t" + descr + "\n")
156
			sys.stdout.flush()
157
			sys.exit(os.EX_OK)
158
		args.append(arg)
134
159
135
	if len(args) != 1:
160
	if len(args) != 1:
136
		sys.stderr.write(usage + "\n")
161
		sys.stderr.write(usage + "\n")
Lines 154-158 if __name__ == "__main__": Link Here
154
179
155
	var_pattern = "^(%s)$" % "|".join(var_pattern)
180
	var_pattern = "^(%s)$" % "|".join(var_pattern)
156
	filter_bash_environment(
181
	filter_bash_environment(
157
		re.compile(var_pattern), file_in, file_out)
182
		re.compile(var_pattern), file_in, file_out, options)
158
	file_out.flush()
183
	file_out.flush()
(-)a/bin/save-ebuild-env.sh (-1 / +1 lines)
Lines 53-59 __save_ebuild_env() { Link Here
53
		einfo einfon ewarn eerror ebegin __eend eend KV_major \
53
		einfo einfon ewarn eerror ebegin __eend eend KV_major \
54
		KV_minor KV_micro KV_to_int get_KV has \
54
		KV_minor KV_micro KV_to_int get_KV has \
55
		__has_phase_defined_up_to \
55
		__has_phase_defined_up_to \
56
		hasv hasq __qa_source __qa_call \
56
		hasv hasq __qa_source __qa_call __call-ebuildshell \
57
		addread addwrite adddeny addpredict __sb_append_var \
57
		addread addwrite adddeny addpredict __sb_append_var \
58
		use usev useq has_version portageq \
58
		use usev useq has_version portageq \
59
		best_version use_with use_enable register_die_hook \
59
		best_version use_with use_enable register_die_hook \
(-)a/man/make.conf.5 (+6 lines)
Lines 382-387 exist). Also see the related \fIunmerge\-backup\fR feature. Link Here
382
Use locks to ensure that unsandboxed ebuild phases never execute
382
Use locks to ensure that unsandboxed ebuild phases never execute
383
concurrently. Also see \fIparallel\-install\fR.
383
concurrently. Also see \fIparallel\-install\fR.
384
.TP
384
.TP
385
.B ebuildshell
386
Drop into an interactive shell for each phase function, meant for
387
debugging.  Because the shell would normally be used to execute the
388
phase function, commands like src_unpack or epatch are available in the
389
interactive shell.  Use `die` to terminate the merge.
390
.TP
385
.B fail\-clean
391
.B fail\-clean
386
Clean up temporary files after a build failure. This is particularly useful
392
Clean up temporary files after a build failure. This is particularly useful
387
if you have \fBPORTAGE_TMPDIR\fR on tmpfs. If this feature is enabled, you
393
if you have \fBPORTAGE_TMPDIR\fR on tmpfs. If this feature is enabled, you
(-)a/pym/_emerge/AbstractEbuildProcess.py (+1 lines)
Lines 161-166 class AbstractEbuildProcess(SpawnProcess): Link Here
161
			self.fd_pipes = {}
161
			self.fd_pipes = {}
162
		null_fd = None
162
		null_fd = None
163
		if 0 not in self.fd_pipes and \
163
		if 0 not in self.fd_pipes and \
164
			"ebuildshell" not in self.settings.features and \
164
			self.phase not in self._phases_interactive_whitelist and \
165
			self.phase not in self._phases_interactive_whitelist and \
165
			"interactive" not in self.settings.get("PROPERTIES", "").split():
166
			"interactive" not in self.settings.get("PROPERTIES", "").split():
166
			null_fd = os.open('/dev/null', os.O_RDONLY)
167
			null_fd = os.open('/dev/null', os.O_RDONLY)
(-)a/pym/portage/const.py (-1 / +1 lines)
Lines 142-147 SUPPORTED_FEATURES = frozenset([ Link Here
142
	"distlocks",
142
	"distlocks",
143
	"downgrade-backup",
143
	"downgrade-backup",
144
	"ebuild-locks",
144
	"ebuild-locks",
145
	"ebuildshell",
145
	"fail-clean",
146
	"fail-clean",
146
	"fakeroot",
147
	"fakeroot",
147
	"fixlafiles",
148
	"fixlafiles",
148
- 

Return to bug 155161