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

Collapse All | Expand All

(-)a/bin/bashrc-functions.sh (+45 lines)
Lines 23-28 register_success_hook() { Link Here
23
	done
23
	done
24
}
24
}
25
25
26
register_phase_hook() {
27
	if [[ -z "$3" ]]; then
28
		echo "!!! register_phase_hook() called without enough parameters." >&2
29
		echo "!!! register_phase_hook <before|after> <phase|all> <cmd>" >&2
30
		return 1
31
	fi
32
	local x when phase cmd cmdargs phase_hooks
33
	when="$(echo $1 | tr 'a-z' 'A-Z')"; shift # uppercase when
34
	phase="$(echo $1 | tr 'A-Z' 'a-z')"; shift # lowercase phase name (to match real phase names)
35
36
	case "${when}" in
37
		BEFORE|AFTER)
38
			: # allowed
39
			;;
40
		*)
41
			echo "!!! register_phase_hook() called with invalid when parameter: $when" >&2
42
			return 1
43
			;;
44
	esac
45
	
46
	phase_hooks="$(eval 'echo $EBUILD_PHASE_HOOKS_'"${when}"'_'"${phase}")"
47
	
48
	if [[ -z "${phase_hooks}" ]]; then
49
		phase_hooks="0 "
50
	elif ! _is_phase_hook_at_version "${phase_hooks}" 0; then
51
		echo "!!! Unsupported ebuild phase hook version"
52
		return $?
53
	fi
54
	
55
	for x in $* ; do
56
		hasq $x ${phase_hooks} || \
57
			phase_hooks+="${x} "
58
	done
59
	
60
	export EBUILD_PHASE_HOOKS_"${when}"_"${phase}"="${phase_hooks}"
61
}
62
63
_is_phase_hook_at_version() {
64
	if [[ "${1:0:1}" == "$2" ]]; then
65
		return 0
66
	else
67
		return 1
68
	fi
69
}
70
26
strip_duplicate_slashes() {
71
strip_duplicate_slashes() {
27
	if [[ -n $1 ]] ; then
72
	if [[ -n $1 ]] ; then
28
		local removed=$1
73
		local removed=$1
(-)a/bin/ebuild.sh (-1 / +12 lines)
Lines 22-28 else Link Here
22
	# the "depend" phase.
22
	# the "depend" phase.
23
	for x in diropts docompress exeopts get_KV insopts \
23
	for x in diropts docompress exeopts get_KV insopts \
24
		keepdir KV_major KV_micro KV_minor KV_to_int \
24
		keepdir KV_major KV_micro KV_minor KV_to_int \
25
		libopts register_die_hook register_success_hook \
25
		libopts register_die_hook register_phase_hook register_success_hook \
26
		remove_path_entry set_unless_changed strip_duplicate_slashes \
26
		remove_path_entry set_unless_changed strip_duplicate_slashes \
27
		unset_unless_changed use_with use_enable ; do
27
		unset_unless_changed use_with use_enable ; do
28
		eval "${x}() {
28
		eval "${x}() {
Lines 394-399 source_all_bashrcs() { Link Here
394
	[ ! -z "${OCXX}" ] && export CXX="${OCXX}"
394
	[ ! -z "${OCXX}" ] && export CXX="${OCXX}"
395
}
395
}
396
396
397
# Portage hooks
398
portage_hooks_pre_ebuild() {
399
	source "${HOOKS_SH_BINARY}" --do-pre-ebuild || return $?
400
}
401
portage_hooks_post_ebuild() {
402
	source "${HOOKS_SH_BINARY}" --do-post-ebuild || return $?
403
}
404
register_phase_hook before all portage_hooks_pre_ebuild
405
register_phase_hook after all portage_hooks_post_ebuild
406
407
397
# === === === === === === === === === === === === === === === === === ===
408
# === === === === === === === === === === === === === === === === === ===
398
# === === === === === functions end, main part begins === === === === ===
409
# === === === === === functions end, main part begins === === === === ===
399
# === === === === === === === === === === === === === === === === === ===
410
# === === === === === === === === === === === === === === === === === ===
(-)a/bin/hooks.sh (+96 lines)
Line 0 Link Here
1
#!/bin/bash
2
# Copyright 1999-2010 Gentoo Foundation
3
# Distributed under the terms of the GNU General Public License v2
4
5
# @MAINTAINER:
6
# jacobgodserv@gmail.com
7
# @BLURB: Executes hooks in the current directory.
8
# @DESCRIPTION:
9
# Part of the portage hooks system, this script is responsible for executing
10
# hooks within a prepared environment
11
12
# Only run hooks if it's requested in $FEATURES
13
if ! (source "${PORTAGE_BIN_PATH}/isolated-functions.sh" && hasq hooks $FEATURES) ; then
14
	return
15
fi
16
17
# TODO: unit testing does not cover this portion of hooks.sh
18
# This code is put here so it's easier to do one-liners elsewhere.
19
# This section is meant to be run by ebuild.sh
20
if [[ "$1" == "--do-pre-ebuild" || "$1" == "--do-post-ebuild" ]]; then
21
	if [[ "${EBUILD_PHASE}" == "" ]]; then
22
		# an in-between-phases moment; useless to hooks
23
		return
24
	fi
25
	
26
27
	oldwd="$(pwd)"
28
	if [[ "$1" == "--do-pre-ebuild" ]]; then
29
		hooks_dir="${PORTAGE_CONFIGROOT}/${HOOKS_PATH}/pre-ebuild.d"
30
	else
31
		hooks_dir="${PORTAGE_CONFIGROOT}/${HOOKS_PATH}/post-ebuild.d"
32
	fi
33
	
34
	[ -d "${hooks_dir}" ] && cd "${hooks_dir}"
35
	exit_code="$?"
36
	if [[ "${exit_code}" != "0" ]]; then
37
		# mimicks behavior in hooks.py
38
		# TODO: --verbose detection?
39
		:
40
		#debug-print "This hook path could not be found; ignored: ${hooks_dir}"
41
	else
42
		# Execute the hooks
43
		source "${HOOKS_SH_BINARY}" --action "${EBUILD_PHASE}" --target "${EBUILD}"
44
		exit_code="$?"
45
		if [[ "${exit_code}" != "0" ]]; then
46
			# mimicks behavior in hooks.py
47
			die "Hook directory ${HOOKS_PATH}/pre-ebuild.d failed with exit code ${exit_code}"
48
		fi
49
	fi
50
	cd "${oldwd}" || die "Could not return to the old ebuild directory after pre-ebuild hooks: ${oldwd}"
51
	
52
	return
53
fi
54
55
# Local variables listed here.
56
# Using the local keyword makes no difference since this script is being sourced
57
# so we'll have to unset them manually later. Be sure to keep the local_vars
58
# array up-to-date.
59
hook_files=( * )
60
hook_args=( "$@" )
61
hook_verbosity="0"
62
hook_rval="0"
63
64
hook_local_vars=( "hook_files" "hook_args" "hook_verbosity" "hook_rval" ) # variables unset for hooks
65
66
for (( i = 0 ; i < ${#hook_args[@]} ; i++ )); do
67
	if [[ "${hook_args[$i]}" == "--verbose" ]]; then
68
		hook_verbosity="1"
69
	fi
70
done
71
72
for (( i = 0 ; i < ${#hook_files[@]} ; i++ )); do
73
	hook="${hook_files[$i]}"
74
	if [[ ! -e "${hook}" ]]; then
75
		continue
76
	elif [[ ! -f "${hook}" ]]; then
77
		[ "${hook_verbosity}" -gt 0 ] && ewarn "Only files are recognized in a hook directory: ${hook}"
78
		continue
79
	fi
80
	
81
	[ "${hook_verbosity}" -gt 0 ] && einfo "Executing hook ${hook}..."
82
	# We use eval so the hook_args gets expanded before it is unset
83
	( eval unset "${hook_local_vars[@]}" '&&' source "${hook}" "${hook_args[@]}" )
84
	
85
	exit_code="$?"
86
	if [[ "${exit_code}" == "3" ]]; then
87
		hook_rval="3"
88
	elif [[ "${exit_code}" != "0" ]]; then
89
		eerror "Hook $(pwd)/${hook} returned with exit code ${exit_code}"
90
		exit "${exit_code}"
91
	fi
92
done
93
94
exit_code="${hook_rval}"
95
unset "${hook_local_vars[@]}"
96
exit "${exit_code}"
(-)a/bin/phase-functions.sh (-3 / +68 lines)
Lines 198-213 preprocess_ebuild_env() { Link Here
198
}
198
}
199
199
200
ebuild_phase() {
200
ebuild_phase() {
201
	declare -F "$1" >/dev/null && qa_call $1
201
	local x phase_name=${1} pre_phase_hooks post_phase_hooks
202
203
	# only run new-style hooks if this function isn't being used to
204
	# execute an old-style phase hook (which causes duplicate new-style
205
	# hook calls)
206
	if [[ ! "${phase_name}" =~ ^pre_|^post_ ]]; then
207
		# Loop through new-style ebuild phase hooks with version check
208
		for x in \
209
			EBUILD_PHASE_HOOKS_BEFORE_"${phase_name}" \
210
			EBUILD_PHASE_HOOKS_BEFORE_all \
211
			EBUILD_PHASE_HOOKS_AFTER_"${phase_name}" \
212
			EBUILD_PHASE_HOOKS_AFTER_all
213
		do
214
			x="$(eval 'echo $'${x})"
215
			if [[ "${x}" == "" ]]; then
216
				continue
217
			fi
218
			if ! _is_phase_hook_at_version "${x}" 0; then
219
				echo "!!! Unsupported ebuild phase hook version"
220
				return 1
221
			fi
222
		done
223
		pre_phase_hooks="$(eval 'echo $EBUILD_PHASE_HOOKS_BEFORE_'"${phase_name}") $EBUILD_PHASE_HOOKS_BEFORE_all"
224
		post_phase_hooks="$(eval 'echo $EBUILD_PHASE_HOOKS_AFTER_'"${phase_name}") $EBUILD_PHASE_HOOKS_AFTER_all"
225
	fi
226
	
227
	for x in \
228
		$pre_phase_hooks \
229
		${phase_name} \
230
		$post_phase_hooks
231
	do
232
		exec_ebuild_phase ${x}
233
	done
202
}
234
}
203
235
236
# TODO: deprecate this function? Should be easy to provide backwards
237
# compatibility:
238
# register_phase_hook before <phase> pre_<phase>
239
# register_phase_hook after <phase> post_<phase>
204
ebuild_phase_with_hooks() {
240
ebuild_phase_with_hooks() {
205
	local x phase_name=${1}
241
	local x phase_name=${1}
206
	for x in {pre_,,post_}${phase_name} ; do
242
	# Loop through new-style ebuild phase hooks with version check
207
		ebuild_phase ${x}
243
	for x in \
244
		EBUILD_PHASE_HOOKS_BEFORE_"${phase_name}" \
245
		EBUILD_PHASE_HOOKS_BEFORE_all \
246
		EBUILD_PHASE_HOOKS_AFTER_"${phase_name}" \
247
		EBUILD_PHASE_HOOKS_AFTER_all
248
	do
249
		x="$(eval 'echo $'${x})"
250
		if [[ "${x}" == "" ]]; then
251
			continue
252
		fi
253
		if ! _is_phase_hook_at_version "${x}" 0; then
254
			echo "!!! Unsupported ebuild phase hook version"
255
			return 1
256
		fi
257
	done
258
	
259
	# Loop through all hooks and the actual phase
260
	for x in \
261
		$(eval 'echo $EBUILD_PHASE_HOOKS_BEFORE_'"${phase_name}") \
262
		$EBUILD_PHASE_HOOKS_BEFORE_all \
263
		{pre_,,post_}${phase_name} \
264
		$(eval 'echo $EBUILD_PHASE_HOOKS_AFTER_'"${phase_name}") \
265
		$EBUILD_PHASE_HOOKS_AFTER_all
266
	do
267
		exec_ebuild_phase ${x}
208
	done
268
	done
209
}
269
}
210
270
271
exec_ebuild_phase() {
272
	declare -F "$1" >/dev/null && qa_call $1
273
}
274
275
211
dyn_pretend() {
276
dyn_pretend() {
212
	if [[ -e $PORTAGE_BUILDDIR/.pretended ]] ; then
277
	if [[ -e $PORTAGE_BUILDDIR/.pretended ]] ; then
213
		vecho ">>> It appears that '$PF' is already pretended; skipping."
278
		vecho ">>> It appears that '$PF' is already pretended; skipping."
(-)a/bin/phase-helpers.sh (-1 / +1 lines)
Lines 188-194 use() { Link Here
188
		# TODO: Add a registration interface for eclasses to register
188
		# TODO: Add a registration interface for eclasses to register
189
		# any number of phase hooks, so that global scope eclass
189
		# any number of phase hooks, so that global scope eclass
190
		# initialization can by migrated to phase hooks in new EAPIs.
190
		# initialization can by migrated to phase hooks in new EAPIs.
191
		# Example: add_phase_hook before pkg_setup $ECLASS_pre_pkg_setup
191
		# Example: register_phase_hook before pkg_setup $ECLASS_pre_pkg_setup
192
		#if [[ -n $EAPI ]] && ! has "$EAPI" 0 1 2 3 ; then
192
		#if [[ -n $EAPI ]] && ! has "$EAPI" 0 1 2 3 ; then
193
		#	die "use() called during invalid phase: $EBUILD_PHASE"
193
		#	die "use() called during invalid phase: $EBUILD_PHASE"
194
		#fi
194
		#fi
(-)a/doc/config.docbook (+1 lines)
Lines 2-5 Link Here
2
<title>Configuration</title>
2
<title>Configuration</title>
3
&config_bashrc;
3
&config_bashrc;
4
&config_set;
4
&config_set;
5
&config_hooks;
5
</part>
6
</part>
(-)a/doc/config/hooks.docbook (+108 lines)
Line 0 Link Here
1
<chapter id='config-hooks'>
2
	<title id="config-hooks.title">Hooks Configuration</title>
3
	
4
	<sect1 id='config-hooks-execution'>
5
		<title id="config-hooks-execution.title">Hooks Execution</title>
6
		
7
		<para>Hooks are only executed if <quote>hooks</quote> is set in
8
		FEATURES.</para>
9
		
10
		<para>
11
		If a hook directory exists, the bash scripts within each one
12
		wil either be executed before or after that particular phase, in
13
		alphabetical order. Each one will receive the environment of an
14
		ebuild, so they are capable of inherit, einfo, and other common
15
		commands (if you find them useful). For non-ebuild hooks, avoid
16
		commands that may trigger changes in the filesystem!
17
		</para>
18
		
19
		<para>
20
		Ebuild hooks are executed within ebuild.sh, so they receive the
21
		same sandbox limitations as ebuilds.
22
		</para>
23
		
24
		<para>
25
		A hook script is expected to understand the following usage:
26
		<cmdsynopsis>
27
			<command>/bin/bash <replaceable>...</replaceable></command><sbr/>
28
29
			<arg>--opt <replaceable>portage arguments, always translated to long form, given by user at the prompt, such as "--verbose" or "--newuse"</replaceable></arg><sbr/>
30
31
			<arg>--action <replaceable>a single action being performed by portage, such as "depclean", "sync", or an ebuild phase</replaceable></arg><sbr/>
32
33
			<arg>--target <replaceable>the thing to perform the action with or on</replaceable></arg>
34
		</cmdsynopsis>
35
		</para>
36
		
37
		<para>
38
		Some hook types have slightly different usage. See <quote>
39
		<link linkend='config-hooks-locations' endterm="config-hooks-locations.title"/></quote> for more
40
		information.
41
		</para>
42
		
43
	</sect1>
44
	
45
	<sect1 id='config-hooks-locations'>
46
		<title id="config-hooks-locations.title">Hooks Locations</title>		
47
		<para>
48
		The following hook directories are supported. Each directory
49
		corresponds to a specific type, such as <quote>ebuild</quote> or
50
		<quote>run</quote>. The standard hook script usage applies given
51
		in <link linkend='config-hooks-execution' endterm="config-hooks-execution.title"/>,
52
		except wherever	described differently below.
53
		</para>
54
		
55
		<itemizedlist>
56
			<listitem><para><filename>/etc/portage/hooks/pre-ebuild.d/</filename> - executed before every ebuild phase execution, within ebuild.sh itself. Never receives --opt, and --target is set to the full path of the ebuild.</para></listitem>
57
			<listitem><para><filename>/etc/portage/hooks/post-ebuild.d/</filename> - executed after every ebuild phase execution. Never receives --opt, and --target is set to the full path of the ebuild.</para></listitem>
58
			<listitem><para><filename>/etc/portage/hooks/pre-run.d/</filename> - executed before portage considers most things, including proper permissions and validity of parsed arguments.</para></listitem>
59
			<listitem><para><filename>/etc/portage/hooks/post-run.d/</filename> - executed after portage is done. It should run regardless of any errors or signals sent, but this cannot be guaranteed for certain scenarios (such as when the KILL signal is received). No information is available concerning the reason portage is exiting. This is a limitation of python itself.</para></listitem>
60
			<listitem><para><filename>/etc/portage/hooks/pre-sync.d/</filename> - executed before portage synchronizes the portage tree.</para></listitem>
61
			<listitem><para><filename>/etc/portage/hooks/post-sync.d/</filename> - executed after portage has <emphasis>successfully</emphasis> synchronized the portage tree. If you want to catch a sync failure, use post-run.</para></listitem>
62
		</itemizedlist>
63
	</sect1>
64
	
65
	<sect1 id='config-hooks-skeleton-hook'>
66
		<title id="config-hooks-skeleton-hook.title">Skeleton Hook</title>
67
		<para>
68
		Most hooks will parse the options at the beginning and look for
69
		specific things. This skeleton hook provides that functionality
70
		to get you started.
71
		</para>
72
		<para>
73
		It's highly recommended that --verbose, --debug, and --quiet be
74
		utilized for suppressing or adding to <quote>regular</quote>
75
		output. The following skeleton hook already has example code in
76
		place to handle these flags.
77
		</para>
78
		<programlisting>
79
		#!/bin/bash
80
81
		verbose_redirect="/dev/null"
82
		debug_redirect="/dev/null"
83
		while [[ "$1" != "" ]]; do
84
			if [[ "$1" == "--opt" ]]; then
85
				if [[ "$2" == "--verbose" ]]; then
86
					verbose_redirect="/dev/tty"
87
				fi
88
				if [[ "$2" == "--debug" ]]; then
89
					debug_redirect="/dev/tty"
90
				fi
91
				if [[ "$2" == "--quiet" ]]; then
92
					verbose_redirect="/dev/null"
93
					debug_redirect="/dev/null"
94
				fi
95
			elif [[ "$1" == "--action" ]]; then
96
				: # do nothing
97
			elif [[ "$1" == "--target" ]]; then
98
				: # do nothing
99
			else
100
				ewarn "Unknown hook option: $1 $2" &amp;> "${verbose_redirect}"
101
			fi
102
			shift 2
103
		done
104
		einfo "This is an example hook." &amp;> "${verbose_redirect}"
105
		einfo "This is debug output." &amp;> "${debug_redirect}"
106
		</programlisting>
107
	</sect1>
108
</chapter>
(-)a/doc/portage.docbook (+1 lines)
Lines 25-30 Link Here
25
	<!ENTITY config SYSTEM "config.docbook">
25
	<!ENTITY config SYSTEM "config.docbook">
26
	<!ENTITY config_bashrc SYSTEM "config/bashrc.docbook">
26
	<!ENTITY config_bashrc SYSTEM "config/bashrc.docbook">
27
	<!ENTITY config_set SYSTEM "config/sets.docbook">
27
	<!ENTITY config_set SYSTEM "config/sets.docbook">
28
	<!ENTITY config_hooks SYSTEM "config/hooks.docbook">
28
]>
29
]>
29
30
30
<book id="portage" lang="en">
31
<book id="portage" lang="en">
(-)a/man/portage.5 (+11 lines)
Lines 66-71 repos.conf Link Here
66
.BR /etc/portage/env/
66
.BR /etc/portage/env/
67
package-specific bashrc files
67
package-specific bashrc files
68
.TP
68
.TP
69
.BR /etc/portage/hooks/
70
portage pre/post hooks
71
.TP
69
.BR /etc/portage/profile/
72
.BR /etc/portage/profile/
70
site-specific overrides of \fB/etc/make.profile/\fR
73
site-specific overrides of \fB/etc/make.profile/\fR
71
.TP
74
.TP
Lines 758-763 in the following order: Link Here
758
/etc/portage/env/${CATEGORY}/${PF}
761
/etc/portage/env/${CATEGORY}/${PF}
759
.RE
762
.RE
760
.TP
763
.TP
764
.BR /etc/portage/hooks/
765
.RS
766
In this directory, portage hooks are executed before each ebuild phase,
767
before and after synchronization, and before and after portage runs
768
themselves. Please see the DocBook documentation for detailed
769
information.
770
.RE
771
.TP
761
.BR /usr/portage/metadata/
772
.BR /usr/portage/metadata/
762
.RS
773
.RS
763
.TP
774
.TP
(-)a/pym/_emerge/actions.py (+8 lines)
Lines 41-46 from portage.output import blue, bold, colorize, create_color_func, darkgreen, \ Link Here
41
	red, yellow
41
	red, yellow
42
good = create_color_func("GOOD")
42
good = create_color_func("GOOD")
43
bad = create_color_func("BAD")
43
bad = create_color_func("BAD")
44
from portage.hooks import HookDirectory
44
from portage.package.ebuild._ipc.QueryCommand import QueryCommand
45
from portage.package.ebuild._ipc.QueryCommand import QueryCommand
45
from portage.package.ebuild.doebuild import _check_temp_dir
46
from portage.package.ebuild.doebuild import _check_temp_dir
46
from portage._sets import load_default_config, SETPREFIX
47
from portage._sets import load_default_config, SETPREFIX
Lines 1969-1974 def action_sync(settings, trees, mtimedb, myopts, myaction): Link Here
1969
	dosyncuri = syncuri
1970
	dosyncuri = syncuri
1970
	updatecache_flg = False
1971
	updatecache_flg = False
1971
	git = False
1972
	git = False
1973
	
1974
	rval = HookDirectory(phase='pre-sync', settings=settings, myopts=myopts, myaction=myaction).execute()
1975
	if rval == 3:
1976
		return 0
1977
	
1972
	if myaction == "metadata":
1978
	if myaction == "metadata":
1973
		print("skipping sync")
1979
		print("skipping sync")
1974
		updatecache_flg = True
1980
		updatecache_flg = True
Lines 2495-2500 def action_sync(settings, trees, mtimedb, myopts, myaction): Link Here
2495
					" %s spawn failed of %s\n" % (bad("*"), postsync,),
2501
					" %s spawn failed of %s\n" % (bad("*"), postsync,),
2496
					level=logging.ERROR, noiselevel=-1)
2502
					level=logging.ERROR, noiselevel=-1)
2497
2503
2504
	HookDirectory(phase='post-sync', settings=settings, myopts=myopts, myaction=myaction).execute()
2505
2498
	if(mybestpv != mypvs) and not "--quiet" in myopts:
2506
	if(mybestpv != mypvs) and not "--quiet" in myopts:
2499
		print()
2507
		print()
2500
		print(red(" * ")+bold("An update to portage is available.")+" It is _highly_ recommended")
2508
		print(red(" * ")+bold("An update to portage is available.")+" It is _highly_ recommended")
(-)a/pym/_emerge/main.py (+13 lines)
Lines 25-30 bad = create_color_func("BAD") Link Here
25
25
26
from portage.const import _ENABLE_DYN_LINK_MAP
26
from portage.const import _ENABLE_DYN_LINK_MAP
27
import portage.elog
27
import portage.elog
28
import portage.hooks
29
import portage.process
28
import portage.util
30
import portage.util
29
import portage.locks
31
import portage.locks
30
import portage.exception
32
import portage.exception
Lines 1613-1619 def emerge_main(args=None): Link Here
1613
	# Portage needs to ensure a sane umask for the files it creates.
1615
	# Portage needs to ensure a sane umask for the files it creates.
1614
	os.umask(0o22)
1616
	os.umask(0o22)
1615
	settings, trees, mtimedb = load_emerge_config()
1617
	settings, trees, mtimedb = load_emerge_config()
1618
1619
	# Portage configured; let's let hooks run before we do anything more
1620
	rval = portage.hooks.HookDirectory(phase='pre-run', settings=settings, myopts=myopts, myaction=myaction, mytargets=myfiles).execute()
1621
	if rval == 3:
1622
		return 0
1623
1624
	settings, trees, mtimedb = load_emerge_config() # once more, since pre-run might've done something
1616
	portdb = trees[settings['EROOT']]['porttree'].dbapi
1625
	portdb = trees[settings['EROOT']]['porttree'].dbapi
1626
1627
	# Have post-run hooks executed whenever portage quits
1628
	portage.process.atexit_register(portage.hooks.HookDirectory(phase='post-run', settings=settings, myopts=myopts, myaction=myaction, mytargets=myfiles).execute)
1629
1617
	rval = profile_check(trees, myaction)
1630
	rval = profile_check(trees, myaction)
1618
	if rval != os.EX_OK:
1631
	if rval != os.EX_OK:
1619
		return rval
1632
		return rval
(-)a/pym/portage/const.py (-1 / +3 lines)
Lines 34-39 CUSTOM_PROFILE_PATH = USER_CONFIG_PATH + "/profile" Link Here
34
USER_VIRTUALS_FILE       = USER_CONFIG_PATH + "/virtuals"
34
USER_VIRTUALS_FILE       = USER_CONFIG_PATH + "/virtuals"
35
EBUILD_SH_ENV_FILE       = USER_CONFIG_PATH + "/bashrc"
35
EBUILD_SH_ENV_FILE       = USER_CONFIG_PATH + "/bashrc"
36
EBUILD_SH_ENV_DIR        = USER_CONFIG_PATH + "/env"
36
EBUILD_SH_ENV_DIR        = USER_CONFIG_PATH + "/env"
37
HOOKS_PATH               = USER_CONFIG_PATH + "/hooks"
37
CUSTOM_MIRRORS_FILE      = USER_CONFIG_PATH + "/mirrors"
38
CUSTOM_MIRRORS_FILE      = USER_CONFIG_PATH + "/mirrors"
38
COLOR_MAP_FILE           = USER_CONFIG_PATH + "/color.map"
39
COLOR_MAP_FILE           = USER_CONFIG_PATH + "/color.map"
39
PROFILE_PATH             = "etc/make.profile"
40
PROFILE_PATH             = "etc/make.profile"
Lines 62-67 PORTAGE_PYM_PATH = PORTAGE_BASE_PATH + "/pym" Link Here
62
LOCALE_DATA_PATH         = PORTAGE_BASE_PATH + "/locale"  # FIXME: not used
63
LOCALE_DATA_PATH         = PORTAGE_BASE_PATH + "/locale"  # FIXME: not used
63
EBUILD_SH_BINARY         = PORTAGE_BIN_PATH + "/ebuild.sh"
64
EBUILD_SH_BINARY         = PORTAGE_BIN_PATH + "/ebuild.sh"
64
MISC_SH_BINARY           = PORTAGE_BIN_PATH + "/misc-functions.sh"
65
MISC_SH_BINARY           = PORTAGE_BIN_PATH + "/misc-functions.sh"
66
HOOKS_SH_BINARY          = PORTAGE_BIN_PATH + "/hooks.sh"
65
SANDBOX_BINARY           = "/usr/bin/sandbox"
67
SANDBOX_BINARY           = "/usr/bin/sandbox"
66
FAKEROOT_BINARY          = "/usr/bin/fakeroot"
68
FAKEROOT_BINARY          = "/usr/bin/fakeroot"
67
BASH_BINARY              = "/bin/bash"
69
BASH_BINARY              = "/bin/bash"
Lines 91-97 SUPPORTED_FEATURES = frozenset([ Link Here
91
                           "collision-protect", "compress-build-logs", "compressdebug",
93
                           "collision-protect", "compress-build-logs", "compressdebug",
92
                           "config-protect-if-modified",
94
                           "config-protect-if-modified",
93
                           "digest", "distcc", "distcc-pump", "distlocks", "ebuild-locks", "fakeroot",
95
                           "digest", "distcc", "distcc-pump", "distlocks", "ebuild-locks", "fakeroot",
94
                           "fail-clean", "force-mirror", "force-prefix", "getbinpkg",
96
                           "fail-clean", "force-mirror", "force-prefix", "getbinpkg", "hooks",
95
                           "installsources", "keeptemp", "keepwork", "fixlafiles", "lmirror",
97
                           "installsources", "keeptemp", "keepwork", "fixlafiles", "lmirror",
96
                           "metadata-transfer", "mirror", "multilib-strict", "news",
98
                           "metadata-transfer", "mirror", "multilib-strict", "news",
97
                           "noauto", "noclean", "nodoc", "noinfo", "noman",
99
                           "noauto", "noclean", "nodoc", "noinfo", "noman",
(-)a/pym/portage/hooks.py (+74 lines)
Line 0 Link Here
1
# Copyright 1999-2011 Gentoo Foundation
2
# Distributed under the terms of the GNU General Public License v2
3
4
from portage.const import BASH_BINARY, HOOKS_PATH, HOOKS_SH_BINARY, PORTAGE_BIN_PATH
5
from portage import os
6
from portage import check_config_instance
7
from portage import normalize_path
8
from portage.exception import PortageException
9
from portage.exception import InvalidLocation
10
from portage.output import EOutput
11
from process import spawn
12
from shutil import rmtree
13
from tempfile import mkdtemp
14
15
class HookDirectory(object):
16
17
	def __init__ (self, phase, settings, myopts=None, myaction=None, mytargets=None):
18
		self.myopts = myopts
19
		self.myaction = myaction
20
		self.mytargets = mytargets
21
		check_config_instance(settings)
22
		self.settings = settings
23
		self.path = os.path.join(settings["PORTAGE_CONFIGROOT"], HOOKS_PATH, phase + '.d')
24
		self.output = EOutput()
25
	
26
	
27
	def execute (self, path=None):
28
		"""
29
		Executes all the hooks for the specified phase
30
31
		@param path: the location where the hooks are stored
32
		@type msg: StringType
33
		
34
		Returns 0 on success, or 3 if success but immediate portage exit
35
		is requested, or throws an exception on error.
36
		"""
37
		if "hooks" not in self.settings['FEATURES']:
38
			return 0
39
		
40
		if not path:
41
			path = self.path
42
		
43
		path = normalize_path(path)
44
		
45
		if not os.path.exists(path):
46
			if self.myopts and "--debug" in self.myopts:
47
				# behavior mimicked by hook.sh
48
				self.output.ewarn('This hook path could not be found; ignored: ' + path)
49
			return 0
50
		
51
		if os.path.isdir(path):
52
			command=[HOOKS_SH_BINARY]
53
			if self.myopts:
54
				for myopt in self.myopts:
55
					command.extend(['--opt', myopt])
56
			if self.myaction:
57
				command.extend(['--action', self.myaction])
58
			if self.mytargets:
59
				for mytarget in self.mytargets:
60
					command.extend(['--target', mytarget])
61
			
62
			command=[BASH_BINARY, '-c', 'cd "'+path+'" && source "' + PORTAGE_BIN_PATH + '/isolated-functions.sh" && source ' + ' '.join(command)]
63
			if self.myopts and "--verbose" in self.myopts:
64
				self.output.einfo('Executing hooks directory "' + self.path + '"...')
65
			code = spawn(mycommand=command, env=self.settings.environ())
66
			if code == 3: # if requesting immediate exit
67
				self.output.ewarn("Hook requested immediate successful exit")
68
				return 3
69
			if code: # if failure
70
				# behavior mimicked by hook.sh
71
				raise PortageException('!!! Hook directory %s failed with exit code %s' % (self.path, code))
72
		
73
		else:
74
			raise InvalidLocation('This hook path ought to be a directory: ' + path)
(-)a/pym/portage/package/ebuild/config.py (-1 / +3 lines)
Lines 23-29 portage.proxy.lazyimport.lazyimport(globals(), Link Here
23
from portage import bsd_chflags, \
23
from portage import bsd_chflags, \
24
	load_mod, os, selinux, _unicode_decode
24
	load_mod, os, selinux, _unicode_decode
25
from portage.const import CACHE_PATH, \
25
from portage.const import CACHE_PATH, \
26
	DEPCACHE_PATH, INCREMENTALS, MAKE_CONF_FILE, \
26
	DEPCACHE_PATH, HOOKS_PATH, HOOKS_SH_BINARY, INCREMENTALS, MAKE_CONF_FILE, \
27
	MODULES_FILE_PATH, \
27
	MODULES_FILE_PATH, \
28
	PRIVATE_PATH, PROFILE_PATH, USER_CONFIG_PATH, \
28
	PRIVATE_PATH, PROFILE_PATH, USER_CONFIG_PATH, \
29
	USER_VIRTUALS_FILE
29
	USER_VIRTUALS_FILE
Lines 501-506 class config(object): Link Here
501
501
502
			self["PORTAGE_CONFIGROOT"] = config_root
502
			self["PORTAGE_CONFIGROOT"] = config_root
503
			self.backup_changes("PORTAGE_CONFIGROOT")
503
			self.backup_changes("PORTAGE_CONFIGROOT")
504
			self["HOOKS_PATH"] = HOOKS_PATH
505
			self.backup_changes("HOOKS_PATH")
504
			self["ROOT"] = target_root
506
			self["ROOT"] = target_root
505
			self.backup_changes("ROOT")
507
			self.backup_changes("ROOT")
506
508
(-)a/pym/portage/tests/hooks/__init__.py (+5 lines)
Line 0 Link Here
1
# tests/portage/hooks/__init__.py -- Portage Unit Test functionality
2
# Copyright 2010 Gentoo Foundation
3
# Distributed under the terms of the GNU General Public License v2
4
# $Id$
5
(-)a/pym/portage/tests/hooks/test_HookDirectory.py (+49 lines)
Line 0 Link Here
1
# test_HookDirectory.py -- Portage Unit Testing Functionality
2
# Copyright 2010 Gentoo Foundation
3
# Distributed under the terms of the GNU General Public License v2
4
# $Id$
5
6
from portage import os
7
from portage.hooks import HookDirectory
8
from portage.package.ebuild.config import config
9
from portage.tests import TestCase
10
from tempfile import mkdtemp
11
from shutil import rmtree
12
13
class HookDirectoryTestCase(TestCase):
14
	
15
	def testHookDirectory(self):
16
		"""
17
		Tests to be sure a hook loads and reads the right settings
18
		Based on test_PackageKeywordsFile.py
19
		"""
20
21
		self.tmp_dir_path = self.BuildTmp('/etc/portage/hooks/test.d')
22
		try:
23
			settings = config()
24
			settings["PORTAGE_CONFIGROOT"] = self.tmp_dir_path
25
			settings["FEATURES"] += " hooks"
26
			hooks = HookDirectory(phase='test', settings=settings)
27
			hooks.execute()
28
			self.assert_(settings["hookonlytest"] == "")
29
		finally:
30
			rmtree(self.tmp_dir_path)
31
	
32
	def BuildTmp(self, tmp_subdir):
33
		tmp_dir = mkdtemp()
34
		hooks_dir = tmp_dir + '/' + tmp_subdir
35
		os.makedirs(hooks_dir)
36
		
37
		f = open(hooks_dir+'/1-testhook', 'w')
38
		f.write('#!/bin/bash\n')
39
		f.write('export hookonlytest="portage cannot see me!"\n')
40
		f.write('exit 0\n')
41
		f.close()
42
		
43
		f = open(hooks_dir+'/2-testhook', 'w')
44
		f.write('#!/bin/bash\n')
45
		f.write('if [[ "${hookonlytest}" != "" ]]; then echo "Unexpected hookonlytest value: ${hookonlytest}"; exit 1; fi\n');
46
		f.write('exit 0\n')
47
		f.close()
48
		
49
		return tmp_dir

Return to bug 272988