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/ebuild.sh (-4 / +72 lines)
Lines 136-142 useq() { Link Here
136
		# TODO: Add a registration interface for eclasses to register
136
		# TODO: Add a registration interface for eclasses to register
137
		# any number of phase hooks, so that global scope eclass
137
		# any number of phase hooks, so that global scope eclass
138
		# initialization can by migrated to phase hooks in new EAPIs.
138
		# initialization can by migrated to phase hooks in new EAPIs.
139
		# Example: add_phase_hook before pkg_setup $ECLASS_pre_pkg_setup
139
		# Example: register_phase_hook before pkg_setup $ECLASS_pre_pkg_setup
140
		#if [[ -n $EAPI ]] && ! hasq "$EAPI" 0 1 2 3 ; then
140
		#if [[ -n $EAPI ]] && ! hasq "$EAPI" 0 1 2 3 ; then
141
		#	die "use() called during invalid phase: $EBUILD_PHASE"
141
		#	die "use() called during invalid phase: $EBUILD_PHASE"
142
		#fi
142
		#fi
Lines 283-288 register_success_hook() { Link Here
283
	done
283
	done
284
}
284
}
285
285
286
register_phase_hook() {
287
	if [ -z "$3" ]; then
288
		echo "!!! register_phase_hook() called without enough parameters." >&2
289
		echo "!!! register_phase_hook <before|after> <phase|all> <cmd>" >&2
290
		return 1
291
	fi
292
	local x when phase cmd cmdargs
293
	when="$(echo $1 | tr 'a-z' 'A-Z')"; shift # uppercase when
294
	phase="$(echo $1 | tr 'A-Z' 'a-z')"; shift # lowercase phase name (to match real phase names)
295
296
	case "${when}" in
297
		BEFORE|AFTER)
298
			: # allowed
299
			;;
300
		*)
301
			echo "!!! register_phase_hook() called with invalid when parameter: $when" >&2
302
			return 1
303
			;;
304
	esac
305
	
306
	for x in $* ; do
307
		hasq $x $(eval 'echo $EBUILD_PHASE_HOOKS_'"${when}"'_'"${phase}") || \
308
			export EBUILD_PHASE_HOOKS_"${when}"_"${phase}"+="${x} "
309
	done
310
}
311
286
# Ensure that $PWD is sane whenever possible, to protect against
312
# Ensure that $PWD is sane whenever possible, to protect against
287
# exploitation of insecure search path for python -c in ebuilds.
313
# exploitation of insecure search path for python -c in ebuilds.
288
# See bug #239560.
314
# See bug #239560.
Lines 655-671 _eapi4_src_install() { Link Here
655
}
681
}
656
682
657
ebuild_phase() {
683
ebuild_phase() {
658
	declare -F "$1" >/dev/null && qa_call $1
684
	local x phase_name=${1} pre_phase_hooks post_phase_hooks
685
686
	# only run new-style hooks if this function isn't being used to
687
	# execute an old-style phase hook (which causes duplicate new-style
688
	# hook calls)
689
	if [[ "$(expr match $1 '^pre_\|^post_')" == "0" ]]; then
690
		pre_phase_hooks="$(eval 'echo $EBUILD_PHASE_HOOKS_BEFORE_'"${phase_name}") $EBUILD_PHASE_HOOKS_BEFORE_all"
691
		post_phase_hooks="$(eval 'echo $EBUILD_PHASE_HOOKS_AFTER_'"${phase_name}") $EBUILD_PHASE_HOOKS_AFTER_all"
692
	fi
693
	
694
	for x in \
695
		$pre_phase_hooks \
696
		${phase_name} \
697
		$post_phase_hooks
698
	do
699
		exec_ebuild_phase ${x}
700
	done
659
}
701
}
660
702
703
# TODO: deprecate this function? Should be easy to provide backwards
704
# compatibility:
705
# register_phase_hook before <phase> pre_<phase>
706
# register_phase_hook after <phase> post_<phase>
661
ebuild_phase_with_hooks() {
707
ebuild_phase_with_hooks() {
662
	local x phase_name=${1}
708
	local x phase_name=${1}
663
	[ -n "$EBUILD_PHASE" ] && rm -f "$T/logging/$EBUILD_PHASE"
709
	[ -n "$EBUILD_PHASE" ] && rm -f "$T/logging/$EBUILD_PHASE"
664
	for x in {pre_,,post_}${phase_name} ; do
710
	for x in \
665
		ebuild_phase ${x}
711
		$(eval 'echo $EBUILD_PHASE_HOOKS_BEFORE_'"${phase_name}") \
712
		$EBUILD_PHASE_HOOKS_BEFORE_all \
713
		pre_${phase_name} \
714
		${phase_name} \
715
		$(eval 'echo $EBUILD_PHASE_HOOKS_AFTER_'"${phase_name}") \
716
		$EBUILD_PHASE_HOOKS_AFTER_all \
717
		post_${phase_name}
718
	do
719
		exec_ebuild_phase ${x}
666
	done
720
	done
667
}
721
}
668
722
723
exec_ebuild_phase() {
724
	declare -F "$1" >/dev/null && qa_call $1
725
}
726
669
dyn_pretend() {
727
dyn_pretend() {
670
	ebuild_phase_with_hooks pkg_pretend
728
	ebuild_phase_with_hooks pkg_pretend
671
}
729
}
Lines 1760-1765 preprocess_ebuild_env() { Link Here
1760
	return ${retval}
1818
	return ${retval}
1761
}
1819
}
1762
1820
1821
# Portage hooks
1822
portage_hooks_pre_ebuild() {
1823
	source "${HOOKS_SH_BINARY}" --do-pre-ebuild || return $?
1824
}
1825
portage_hooks_post_ebuild() {
1826
	source "${HOOKS_SH_BINARY}" --do-post-ebuild || return $?
1827
}
1828
register_phase_hook before all portage_hooks_pre_ebuild
1829
register_phase_hook after all portage_hooks_post_ebuild
1830
1763
# === === === === === === === === === === === === === === === === === ===
1831
# === === === === === === === === === === === === === === === === === ===
1764
# === === === === === functions end, main part begins === === === === ===
1832
# === === === === === functions end, main part begins === === === === ===
1765
# === === === === === functions end, main part begins === === === === ===
1833
# === === === === === functions end, main part begins === === === === ===
(-)a/bin/hooks.sh (+91 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
63
hook_local_vars=( "hook_files" "hook_args" "hook_verbosity" ) # variables unset for hooks
64
65
for (( i = 0 ; i < ${#hook_args[@]} ; i++ )); do
66
	if [[ "${hook_args[$i]}" == "--verbose" ]]; then
67
		hook_verbosity="1"
68
	fi
69
done
70
71
for (( i = 0 ; i < ${#hook_files[@]} ; i++ )); do
72
	hook="${hook_files[$i]}"
73
	if [[ ! -e "${hook}" ]]; then
74
		continue
75
	elif [[ ! -f "${hook}" ]]; then
76
		[ "${hook_verbosity}" -gt 0 ] && ewarn "Only files are recognized in a hook directory: ${hook}"
77
		continue
78
	fi
79
	
80
	[ "${hook_verbosity}" -gt 0 ] && einfo "Executing hook ${hook}..."
81
	# We use eval so the hook_args gets expanded before it is unset
82
	( eval unset "${hook_local_vars[@]}" '&&' source "${hook}" "${hook_args[@]}" )
83
	
84
	exit_code="$?"
85
	if [[ "${exit_code}" != "0" ]]; then
86
		eerror "Hook $(pwd)/${hook} returned with exit code ${exit_code}"
87
		exit "${exit_code}"
88
	fi
89
done
90
91
unset "${hook_local_vars[@]}"
(-)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 23-28 Link Here
23
	<!ENTITY config SYSTEM "config.docbook">
23
	<!ENTITY config SYSTEM "config.docbook">
24
	<!ENTITY config_bashrc SYSTEM "config/bashrc.docbook">
24
	<!ENTITY config_bashrc SYSTEM "config/bashrc.docbook">
25
	<!ENTITY config_set SYSTEM "config/sets.docbook">
25
	<!ENTITY config_set SYSTEM "config/sets.docbook">
26
	<!ENTITY config_hooks SYSTEM "config/hooks.docbook">
26
]>
27
]>
27
28
28
<book id="portage" lang="en">
29
<book id="portage" lang="en">
(-)a/man/portage.5 (+11 lines)
Lines 62-67 repos.conf Link Here
62
.BR /etc/portage/env/
62
.BR /etc/portage/env/
63
package-specific bashrc files
63
package-specific bashrc files
64
.TP
64
.TP
65
.BR /etc/portage/hooks/
66
portage pre/post hooks
67
.TP
65
.BR /etc/portage/profile/
68
.BR /etc/portage/profile/
66
site-specific overrides of \fB/etc/make.profile/\fR
69
site-specific overrides of \fB/etc/make.profile/\fR
67
.TP
70
.TP
Lines 637-642 order: Link Here
637
/etc/portage/env/${CATEGORY}/${PF}
640
/etc/portage/env/${CATEGORY}/${PF}
638
.RE
641
.RE
639
.TP
642
.TP
643
.BR /etc/portage/hooks/
644
.RS
645
In this directory, portage hooks are executed before each ebuild phase,
646
before and after synchronization, and before and after portage runs
647
themselves. Please see the DocBook documentation for detailed
648
information.
649
.RE
650
.TP
640
.BR /usr/portage/metadata/
651
.BR /usr/portage/metadata/
641
.RS
652
.RS
642
.TP
653
.TP
(-)a/pym/_emerge/actions.py (+4 lines)
Lines 33-38 from portage.output import blue, bold, colorize, create_color_func, darkgreen, \ Link Here
33
	red, yellow
33
	red, yellow
34
good = create_color_func("GOOD")
34
good = create_color_func("GOOD")
35
bad = create_color_func("BAD")
35
bad = create_color_func("BAD")
36
from portage.hooks import HookDirectory
36
from portage.sets import load_default_config, SETPREFIX
37
from portage.sets import load_default_config, SETPREFIX
37
from portage.sets.base import InternalPackageSet
38
from portage.sets.base import InternalPackageSet
38
from portage.util import cmp_sort_key, writemsg, writemsg_level
39
from portage.util import cmp_sort_key, writemsg, writemsg_level
Lines 1836-1841 def action_sync(settings, trees, mtimedb, myopts, myaction): Link Here
1836
	os.umask(0o022)
1837
	os.umask(0o022)
1837
	dosyncuri = syncuri
1838
	dosyncuri = syncuri
1838
	updatecache_flg = False
1839
	updatecache_flg = False
1840
	HookDirectory(phase='pre-sync', settings=settings, myopts=myopts, myaction=myaction).execute()
1839
	if myaction == "metadata":
1841
	if myaction == "metadata":
1840
		print("skipping sync")
1842
		print("skipping sync")
1841
		updatecache_flg = True
1843
		updatecache_flg = True
Lines 2279-2284 def action_sync(settings, trees, mtimedb, myopts, myaction): Link Here
2279
			if retval != os.EX_OK:
2281
			if retval != os.EX_OK:
2280
				print(red(" * ") + bold("spawn failed of " + postsync))
2282
				print(red(" * ") + bold("spawn failed of " + postsync))
2281
2283
2284
	HookDirectory(phase='post-sync', settings=settings, myopts=myopts, myaction=myaction).execute()
2285
2282
	if(mybestpv != mypvs) and not "--quiet" in myopts:
2286
	if(mybestpv != mypvs) and not "--quiet" in myopts:
2283
		print()
2287
		print()
2284
		print(red(" * ")+bold("An update to portage is available.")+" It is _highly_ recommended")
2288
		print(red(" * ")+bold("An update to portage is available.")+" It is _highly_ recommended")
(-)a/pym/_emerge/main.py (+11 lines)
Lines 27-32 bad = create_color_func("BAD") Link Here
27
import portage.elog
27
import portage.elog
28
import portage.dep
28
import portage.dep
29
portage.dep._dep_check_strict = True
29
portage.dep._dep_check_strict = True
30
import portage.hooks
31
import portage.process
30
import portage.util
32
import portage.util
31
import portage.locks
33
import portage.locks
32
import portage.exception
34
import portage.exception
Lines 1232-1238 def emerge_main(): Link Here
1232
	# Portage needs to ensure a sane umask for the files it creates.
1234
	# Portage needs to ensure a sane umask for the files it creates.
1233
	os.umask(0o22)
1235
	os.umask(0o22)
1234
	settings, trees, mtimedb = load_emerge_config()
1236
	settings, trees, mtimedb = load_emerge_config()
1237
1238
	# Portage configured; let's let hooks run before we do anything more
1239
	portage.hooks.HookDirectory(phase='pre-run', settings=settings, myopts=myopts, myaction=myaction, mytargets=myfiles).execute()
1240
1241
	settings, trees, mtimedb = load_emerge_config() # once more, since pre-run might've done something
1235
	portdb = trees[settings["ROOT"]]["porttree"].dbapi
1242
	portdb = trees[settings["ROOT"]]["porttree"].dbapi
1243
1244
	# Have post-run hooks executed whenever portage quits
1245
	portage.process.atexit_register(portage.hooks.HookDirectory(phase='post-run', settings=settings, myopts=myopts, myaction=myaction, mytargets=myfiles).execute)
1246
1236
	rval = profile_check(trees, myaction)
1247
	rval = profile_check(trees, myaction)
1237
	if rval != os.EX_OK:
1248
	if rval != os.EX_OK:
1238
		return rval
1249
		return rval
(-)a/pym/portage/const.py (-1 / +3 lines)
Lines 35-40 CUSTOM_PROFILE_PATH = USER_CONFIG_PATH + "/profile" Link Here
35
USER_VIRTUALS_FILE       = USER_CONFIG_PATH + "/virtuals"
35
USER_VIRTUALS_FILE       = USER_CONFIG_PATH + "/virtuals"
36
EBUILD_SH_ENV_FILE       = USER_CONFIG_PATH + "/bashrc"
36
EBUILD_SH_ENV_FILE       = USER_CONFIG_PATH + "/bashrc"
37
EBUILD_SH_ENV_DIR        = USER_CONFIG_PATH + "/env"
37
EBUILD_SH_ENV_DIR        = USER_CONFIG_PATH + "/env"
38
HOOKS_PATH               = USER_CONFIG_PATH + "/hooks"
38
CUSTOM_MIRRORS_FILE      = USER_CONFIG_PATH + "/mirrors"
39
CUSTOM_MIRRORS_FILE      = USER_CONFIG_PATH + "/mirrors"
39
COLOR_MAP_FILE           = USER_CONFIG_PATH + "/color.map"
40
COLOR_MAP_FILE           = USER_CONFIG_PATH + "/color.map"
40
PROFILE_PATH             = "etc/make.profile"
41
PROFILE_PATH             = "etc/make.profile"
Lines 60-65 PORTAGE_PYM_PATH = PORTAGE_BASE_PATH + "/pym" Link Here
60
LOCALE_DATA_PATH         = PORTAGE_BASE_PATH + "/locale"  # FIXME: not used
61
LOCALE_DATA_PATH         = PORTAGE_BASE_PATH + "/locale"  # FIXME: not used
61
EBUILD_SH_BINARY         = PORTAGE_BIN_PATH + "/ebuild.sh"
62
EBUILD_SH_BINARY         = PORTAGE_BIN_PATH + "/ebuild.sh"
62
MISC_SH_BINARY           = PORTAGE_BIN_PATH + "/misc-functions.sh"
63
MISC_SH_BINARY           = PORTAGE_BIN_PATH + "/misc-functions.sh"
64
HOOKS_SH_BINARY          = PORTAGE_BIN_PATH + "/hooks.sh"
63
SANDBOX_BINARY           = "/usr/bin/sandbox"
65
SANDBOX_BINARY           = "/usr/bin/sandbox"
64
FAKEROOT_BINARY          = "/usr/bin/fakeroot"
66
FAKEROOT_BINARY          = "/usr/bin/fakeroot"
65
BASH_BINARY              = "/bin/bash"
67
BASH_BINARY              = "/bin/bash"
Lines 85-91 EBUILD_PHASES = ("pretend", "setup", "unpack", "prepare", "configure" Link Here
85
SUPPORTED_FEATURES       = frozenset([
87
SUPPORTED_FEATURES       = frozenset([
86
                           "assume-digests", "buildpkg", "buildsyspkg", "ccache",
88
                           "assume-digests", "buildpkg", "buildsyspkg", "ccache",
87
                           "collision-protect", "digest", "distcc", "distlocks",
89
                           "collision-protect", "digest", "distcc", "distlocks",
88
                           "fakeroot", "fail-clean", "fixpackages", "getbinpkg",
90
                           "fakeroot", "fail-clean", "fixpackages", "hooks", "getbinpkg",
89
                           "installsources", "keeptemp", "keepwork", "lmirror",
91
                           "installsources", "keeptemp", "keepwork", "lmirror",
90
                           "metadata-transfer", "mirror", "multilib-strict", "news",
92
                           "metadata-transfer", "mirror", "multilib-strict", "news",
91
                           "noauto", "noclean", "nodoc", "noinfo", "noman", "nostrip"
93
                           "noauto", "noclean", "nodoc", "noinfo", "noman", "nostrip"
(-)a/pym/portage/hooks.py (+78 lines)
Line 0 Link Here
1
# Copyright 1998-2010 Gentoo Foundation
2
# Distributed under the terms of the GNU General Public License v2
3
# $Id$
4
5
from portage.const import BASH_BINARY, HOOKS_PATH, HOOKS_SH_BINARY, PORTAGE_BIN_PATH
6
from portage import os
7
from portage import check_config_instance
8
from portage import normalize_path
9
from portage.exception import PortageException
10
from portage.exception import InvalidLocation
11
from portage.output import EOutput
12
from process import spawn
13
from shutil import rmtree
14
from tempfile import mkdtemp
15
16
class HookDirectory(object):
17
18
	def __init__ (self, phase, settings, myopts=None, myaction=None, mytargets=None):
19
		self.myopts = myopts
20
		self.myaction = myaction
21
		self.mytargets = mytargets
22
		check_config_instance(settings)
23
		self.settings = settings
24
		self.path = os.path.join(settings["PORTAGE_CONFIGROOT"], HOOKS_PATH, phase + '.d')
25
		self.output = EOutput()
26
27
	def execute (self, path=None):
28
		if "hooks" not in self.settings['FEATURES']:
29
			return
30
		
31
		if not path:
32
			path = self.path
33
		
34
		path = normalize_path(path)
35
		
36
		if not os.path.exists(path):
37
			if self.myopts and "--debug" in self.myopts:
38
				# behavior mimicked by hook.sh
39
				self.output.ewarn('This hook path could not be found; ignored: ' + path)
40
			return
41
		
42
		if os.path.isdir(path):
43
			command=[HOOKS_SH_BINARY]
44
			if self.myopts:
45
				for myopt in self.myopts:
46
					command.extend(['--opt', myopt])
47
			if self.myaction:
48
				command.extend(['--action', self.myaction])
49
			if self.mytargets:
50
				for mytarget in self.mytargets:
51
					command.extend(['--target', mytarget])
52
			
53
			command=[BASH_BINARY, '-c', 'cd "'+path+'" && source "' + PORTAGE_BIN_PATH + '/isolated-functions.sh" && source ' + ' '.join(command)]
54
			if self.myopts and "--verbose" in self.myopts:
55
				self.output.einfo('Executing hooks directory "' + self.path + '"...')
56
			code = spawn(mycommand=command, env=self.settings.environ())
57
			if code: # if failure
58
				# behavior mimicked by hook.sh
59
				raise PortageException('!!! Hook directory %s failed with exit code %s' % (self.path, code))
60
		
61
		else:
62
			raise InvalidLocation('This hook path ought to be a directory: ' + path)
63
	
64
	def merge_to_env (self, existingenv, path):
65
		path = normalize_path(path)
66
67
		if not os.path.isdir(path):
68
			raise InvalidLocation('This environment path is not a directory: ' + path)
69
		
70
		for parent, dirs, files in os.walk(path):
71
			for varname in files:
72
				file = open(os.path.join(path, varname), 'r')
73
				# read the file, remove the very last newline, and make the escaped double-quotes just plain double-quotes (since only bash needs them to be escaped, not python)
74
				vardata = file.read()[:-1].replace('\"','"').strip('"')
75
				existingenv[varname] = vardata
76
				existingenv.backup_changes(varname)
77
		
78
		return existingenv
(-)a/pym/portage/package/ebuild/config.py (-4 / +8 lines)
Lines 22-31 import portage Link Here
22
from portage import bsd_chflags, eapi_is_supported, \
22
from portage import bsd_chflags, eapi_is_supported, \
23
	load_mod, os, selinux, _encodings, _unicode_encode, _unicode_decode
23
	load_mod, os, selinux, _encodings, _unicode_encode, _unicode_decode
24
from portage.const import CACHE_PATH, CUSTOM_PROFILE_PATH, \
24
from portage.const import CACHE_PATH, CUSTOM_PROFILE_PATH, \
25
	DEPCACHE_PATH, GLOBAL_CONFIG_PATH, INCREMENTALS, MAKE_CONF_FILE, \
25
	DEPCACHE_PATH, GLOBAL_CONFIG_PATH, HOOKS_PATH, HOOKS_SH_BINARY, \
26
	MODULES_FILE_PATH, PORTAGE_BIN_PATH, PORTAGE_PYM_PATH, \
26
	INCREMENTALS, MAKE_CONF_FILE, MODULES_FILE_PATH, PORTAGE_BIN_PATH, \
27
	PRIVATE_PATH, PROFILE_PATH, SUPPORTED_FEATURES, USER_CONFIG_PATH, \
27
	PORTAGE_PYM_PATH, PRIVATE_PATH, PROFILE_PATH, SUPPORTED_FEATURES, \
28
	USER_VIRTUALS_FILE
28
	USER_CONFIG_PATH, USER_VIRTUALS_FILE
29
from portage.data import portage_gid
29
from portage.data import portage_gid
30
from portage.dbapi import dbapi
30
from portage.dbapi import dbapi
31
from portage.dbapi.porttree import portdbapi
31
from portage.dbapi.porttree import portdbapi
Lines 762-767 class config(object): Link Here
762
762
763
			self["PORTAGE_CONFIGROOT"] = config_root
763
			self["PORTAGE_CONFIGROOT"] = config_root
764
			self.backup_changes("PORTAGE_CONFIGROOT")
764
			self.backup_changes("PORTAGE_CONFIGROOT")
765
			self["HOOKS_PATH"] = HOOKS_PATH
766
			self.backup_changes("HOOKS_PATH")
765
			self["ROOT"] = target_root
767
			self["ROOT"] = target_root
766
			self.backup_changes("ROOT")
768
			self.backup_changes("ROOT")
767
769
Lines 987-992 class config(object): Link Here
987
989
988
			self["PORTAGE_BIN_PATH"] = PORTAGE_BIN_PATH
990
			self["PORTAGE_BIN_PATH"] = PORTAGE_BIN_PATH
989
			self.backup_changes("PORTAGE_BIN_PATH")
991
			self.backup_changes("PORTAGE_BIN_PATH")
992
			self["HOOKS_SH_BINARY"] = HOOKS_SH_BINARY
993
			self.backup_changes("HOOKS_SH_BINARY")
990
			self["PORTAGE_PYM_PATH"] = PORTAGE_PYM_PATH
994
			self["PORTAGE_PYM_PATH"] = PORTAGE_PYM_PATH
991
			self.backup_changes("PORTAGE_PYM_PATH")
995
			self.backup_changes("PORTAGE_PYM_PATH")
992
996
(-)a/pym/portage/package/ebuild/doebuild.py (-1 / +3 lines)
Lines 21-26 import time Link Here
21
21
22
import portage
22
import portage
23
portage.proxy.lazyimport.lazyimport(globals(),
23
portage.proxy.lazyimport.lazyimport(globals(),
24
	'portage.package.ebuild.const:HOOKS_SH_BINARY',
24
	'portage.package.ebuild.config:check_config_instance',
25
	'portage.package.ebuild.config:check_config_instance',
25
	'portage.package.ebuild.digestcheck:digestcheck',
26
	'portage.package.ebuild.digestcheck:digestcheck',
26
	'portage.package.ebuild.digestgen:digestgen',
27
	'portage.package.ebuild.digestgen:digestgen',
Lines 44-49 from portage.elog.messages import eerror, eqawarn Link Here
44
from portage.exception import DigestException, FileNotFound, \
45
from portage.exception import DigestException, FileNotFound, \
45
	IncorrectParameter, InvalidAtom, InvalidDependString, PermissionDenied, \
46
	IncorrectParameter, InvalidAtom, InvalidDependString, PermissionDenied, \
46
	UnsupportedAPIException
47
	UnsupportedAPIException
48
from portage.hooks import HookDirectory
47
from portage.localization import _
49
from portage.localization import _
48
from portage.manifest import Manifest
50
from portage.manifest import Manifest
49
from portage.output import style_to_ansi_code
51
from portage.output import style_to_ansi_code
Lines 1041-1047 def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, Link Here
1041
			# If necessary, depend phase has been triggered by aux_get calls
1043
			# If necessary, depend phase has been triggered by aux_get calls
1042
			# and the exemption is no longer needed.
1044
			# and the exemption is no longer needed.
1043
			portage._doebuild_manifest_exempt_depend -= 1
1045
			portage._doebuild_manifest_exempt_depend -= 1
1044
1046
		
1045
def _validate_deps(mysettings, myroot, mydo, mydbapi):
1047
def _validate_deps(mysettings, myroot, mydo, mydbapi):
1046
1048
1047
	invalid_dep_exempt_phases = \
1049
	invalid_dep_exempt_phases = \
(-)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