diff options
author | Martin Jansa <Martin.Jansa@gmail.com> | 2013-07-06 15:43:00 +0200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2013-07-29 13:09:05 +0100 |
commit | 5c4513daf653b632096176f4af21706c66e716cc (patch) | |
tree | 7d9e57b5d44ad28af83344013cabaf87747ceb75 /scripts | |
parent | 462acd212a9e7364ce329a4fc70aa09ab93628b3 (diff) | |
download | poky-5c4513daf653b632096176f4af21706c66e716cc.tar.gz |
test-dependencies: add simple script to detect missing or autoenabled dependencies
(From OE-Core rev: a2b3c9e01c871a395a93e162731db77a618306cb)
Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
Signed-off-by: Saul Wold <sgw@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/test-dependencies.sh | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/scripts/test-dependencies.sh b/scripts/test-dependencies.sh new file mode 100755 index 0000000000..e4cf4d366b --- /dev/null +++ b/scripts/test-dependencies.sh | |||
@@ -0,0 +1,256 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | # Author: Martin Jansa <martin.jansa@gmail.com> | ||
4 | # | ||
5 | # Copyright (c) 2013 Martin Jansa <Martin.Jansa@gmail.com> | ||
6 | |||
7 | # Used to detect missing dependencies or automagically | ||
8 | # enabled dependencies which aren't explicitly enabled | ||
9 | # or disabled. | ||
10 | |||
11 | # It does 3 builds of <target> | ||
12 | # 1st to populate sstate-cache directory and sysroot | ||
13 | # 2nd to rebuild each recipe with every possible | ||
14 | # dependency found in sysroot (which stays populated | ||
15 | # from 1st build | ||
16 | # 3rd to rebuild each recipe only with dependencies defined | ||
17 | # in DEPENDS | ||
18 | # 4th (optional) repeat build like 3rd to make sure that | ||
19 | # minimal versions of dependencies defined in DEPENDS | ||
20 | # is also enough | ||
21 | |||
22 | # Global vars | ||
23 | tmpdir= | ||
24 | targets= | ||
25 | recipes= | ||
26 | buildhistory= | ||
27 | buildtype= | ||
28 | default_targets="world" | ||
29 | default_buildhistory="buildhistory" | ||
30 | default_buildtype="1 2 3 c" | ||
31 | |||
32 | usage () { | ||
33 | cat << EOF | ||
34 | Welcome to utility to detect missing or autoenabled dependencies. | ||
35 | WARNING: this utility will completely remove your tmpdir (make sure | ||
36 | you don't have important buildhistory or persistent dir there). | ||
37 | $0 <OPTION> | ||
38 | |||
39 | Options: | ||
40 | -h, --help | ||
41 | Display this help and exit. | ||
42 | |||
43 | --tmpdir=<tmpdir> | ||
44 | Specify tmpdir, will use the environment variable TMPDIR if it is not specified. | ||
45 | Something like /OE/oe-core/tmp-eglibc (no / at the end). | ||
46 | |||
47 | --targets=<targets> | ||
48 | List of targets separated by space, will use the environment variable TARGETS if it is not specified. | ||
49 | It will run "bitbake <targets>" to populate sysroots. | ||
50 | Default value is "world". | ||
51 | |||
52 | --recipes=<recipes> | ||
53 | File with list of recipes we want to rebuild with minimal and maximal sysroot. | ||
54 | Will use the environment variable RECIPES if it is not specified. | ||
55 | Default value will use all packages ever recorded in buildhistory directory. | ||
56 | |||
57 | --buildhistory=<buildhistory> | ||
58 | Path to buildhistory directory, it needs to be enabled in your config, | ||
59 | because it's used to detect different dependencies and to create list | ||
60 | of recipes to rebuild when it's not specified. | ||
61 | Will use the environment variable BUILDHISTORY if it is not specified. | ||
62 | Default value is "buildhistory" | ||
63 | |||
64 | --buildtype=<buildtype> | ||
65 | There are 4 types of build: | ||
66 | 1: build to populate sstate-cache directory and sysroot | ||
67 | 2: build to rebuild each recipe with every possible dep | ||
68 | 3: build to rebuild each recipe with minimal dependencies | ||
69 | 4: build to rebuild each recipe again with minimal dependencies | ||
70 | c: compare buildhistory directories from build 2 and 3 | ||
71 | Will use the environment variable BUILDTYPE if it is not specified. | ||
72 | Default value is "1 2 3 c", order is important, type 4 is optional. | ||
73 | EOF | ||
74 | } | ||
75 | |||
76 | # Print error information and exit. | ||
77 | echo_error () { | ||
78 | echo "ERROR: $1" >&2 | ||
79 | exit 1 | ||
80 | } | ||
81 | |||
82 | while [ -n "$1" ]; do | ||
83 | case $1 in | ||
84 | --tmpdir=*) | ||
85 | tmpdir=`echo $1 | sed -e 's#^--tmpdir=##' | xargs readlink -e` | ||
86 | [ -d "$tmpdir" ] || echo_error "Invalid argument to --tmpdir" | ||
87 | shift | ||
88 | ;; | ||
89 | --targets=*) | ||
90 | targets=`echo $1 | sed -e 's#^--targets="*\([^"]*\)"*#\1#'` | ||
91 | shift | ||
92 | ;; | ||
93 | --recipes=*) | ||
94 | recipes=`echo $1 | sed -e 's#^--recipes="*\([^"]*\)"*#\1#'` | ||
95 | shift | ||
96 | ;; | ||
97 | --buildhistory=*) | ||
98 | buildhistory=`echo $1 | sed -e 's#^--buildhistory="*\([^"]*\)"*#\1#'` | ||
99 | shift | ||
100 | ;; | ||
101 | --buildtype=*) | ||
102 | buildtype=`echo $1 | sed -e 's#^--buildtype="*\([^"]*\)"*#\1#'` | ||
103 | shift | ||
104 | ;; | ||
105 | --help|-h) | ||
106 | usage | ||
107 | exit 0 | ||
108 | ;; | ||
109 | *) | ||
110 | echo "Invalid arguments $*" | ||
111 | echo_error "Try '$0 -h' for more information." | ||
112 | ;; | ||
113 | esac | ||
114 | done | ||
115 | |||
116 | # tmpdir directory, use environment variable TMPDIR | ||
117 | # if it was not specified, otherwise, error. | ||
118 | [ -n "$tmpdir" ] || tmpdir=$TMPDIR | ||
119 | [ -n "$tmpdir" ] || echo_error "No tmpdir found!" | ||
120 | [ -d "$tmpdir" ] || echo_error "Invalid tmpdir \"$tmpdir\"" | ||
121 | [ -n "$targets" ] || targets=$TARGETS | ||
122 | [ -n "$targets" ] || targets=$default_targets | ||
123 | [ -n "$recipes" ] || recipes=$RECIPES | ||
124 | [ -n "$recipes" -a ! -f "$recipes" ] && echo_error "Invalid file with list of recipes to rebuild" | ||
125 | [ -n "$recipes" ] || echo "All packages ever recorded in buildhistory directory will be rebuilt" | ||
126 | [ -n "$buildhistory" ] || buildhistory=$BUILDHISTORY | ||
127 | [ -n "$buildhistory" ] || buildhistory=$default_buildhistory | ||
128 | [ -d "$buildhistory" ] || echo_error "Invalid buildhistory directory \"$buildhistory\"" | ||
129 | [ -n "$buildtype" ] || buildtype=$BUILDTYPE | ||
130 | [ -n "$buildtype" ] || buildtype=$default_buildtype | ||
131 | echo "$buildtype" | grep -v '^[1234c ]*$' && echo_error "Invalid buildtype \"$buildtype\", only some combination of 1, 2, 3, 4, c separated by space is allowed" | ||
132 | |||
133 | OUTPUT_BASE=test-dependencies/`date "+%s"` | ||
134 | |||
135 | build_all() { | ||
136 | echo "===== 1st build to populate sstate-cache directory and sysroot =====" | ||
137 | OUTPUT1=${OUTPUT_BASE}/${TYPE}_all | ||
138 | mkdir -p ${OUTPUT1} | ||
139 | echo "Logs will be stored in ${OUTPUT1} directory" | ||
140 | bitbake -k $targets | tee -a ${OUTPUT1}/complete.log | ||
141 | } | ||
142 | |||
143 | build_every_recipe() { | ||
144 | if [ "${TYPE}" = "2" ] ; then | ||
145 | echo "===== 2nd build to rebuild each recipe with every possible dep =====" | ||
146 | OUTPUT_MAX=${OUTPUT_BASE}/${TYPE}_max | ||
147 | OUTPUTB=${OUTPUT_MAX} | ||
148 | else | ||
149 | echo "===== 3rd or 4th build to rebuild each recipe with minimal dependencies =====" | ||
150 | OUTPUT_MIN=${OUTPUT_BASE}/${TYPE}_min | ||
151 | OUTPUTB=${OUTPUT_MIN} | ||
152 | fi | ||
153 | |||
154 | mkdir -p ${OUTPUTB} ${OUTPUTB}/failed ${OUTPUTB}/ok | ||
155 | echo "Logs will be stored in ${OUTPUTB} directory" | ||
156 | if [ -z "$recipes" ]; then | ||
157 | ls -d $buildhistory/packages/*/* | xargs -n 1 basename | sort -u > ${OUTPUTB}/recipe.list | ||
158 | recipes=${OUTPUTB}/recipe.list | ||
159 | fi | ||
160 | if [ "${TYPE}" != "2" ] ; then | ||
161 | echo "!!!Removing tmpdir \"$tmpdir\"!!!" | ||
162 | rm -rf $tmpdir/deploy $tmpdir/pkgdata $tmpdir/sstate-control $tmpdir/stamps $tmpdir/sysroots $tmpdir/work $tmpdir/work-shared 2>/dev/null | ||
163 | fi | ||
164 | i=1 | ||
165 | count=`cat $recipes | wc -l` | ||
166 | for recipe in `cat $recipes`; do | ||
167 | echo "Building recipe: ${recipe} ($i/$count)" | ||
168 | bitbake -c cleansstate ${recipe} > ${OUTPUTB}/log.${recipe} 2>&1; | ||
169 | bitbake ${recipe} >> ${OUTPUTB}/log.${recipe} 2>&1; | ||
170 | grep "ERROR: Task.*failed" ${OUTPUTB}/log.${recipe} && mv ${OUTPUTB}/log.${recipe} ${OUTPUTB}/failed/${recipe} || mv ${OUTPUTB}/log.${recipe} ${OUTPUTB}/ok/${recipe} | ||
171 | if [ "${TYPE}" != "2" ] ; then | ||
172 | rm -rf $tmpdir/deploy $tmpdir/pkgdata $tmpdir/sstate-control $tmpdir/stamps $tmpdir/sysroots $tmpdir/work $tmpdir/work-shared 2>/dev/null | ||
173 | fi | ||
174 | i=`expr $i + 1` | ||
175 | done | ||
176 | echo "Copying buildhistory/packages to ${OUTPUTB}" | ||
177 | cp -ra $buildhistory/packages ${OUTPUTB} | ||
178 | # This will be usefull to see which library is pulling new dependency | ||
179 | echo "Copying do_package logs to ${OUTPUTB}/do_package/" | ||
180 | mkdir ${OUTPUTB}/do_package | ||
181 | find $tmpdir/work/ -name log.do_package | while read f; do | ||
182 | # pn is 3 levels back, but we don't know if there is just one log per pn (only one arch and version) | ||
183 | # dest=`echo $f | sed 's#^.*/\([^/]*\)/\([^/]*\)/\([^/]*\)/log.do_package#\1#g'` | ||
184 | dest=`echo $f | sed "s#$tmpdir/work/##g; s#/#_#g"` | ||
185 | cp $f ${OUTPUTB}/do_package/$dest | ||
186 | done | ||
187 | grep "ERROR: Task.*failed" ${OUTPUTB}/failed/* | ||
188 | ls -1 ${OUTPUTB}/failed/* >> ${OUTPUT_BASE}/failed.recipes | ||
189 | } | ||
190 | |||
191 | compare_deps() { | ||
192 | # you can run just compare task with command like this | ||
193 | # OUTPUT_BASE=test-dependencies/1373140172 \ | ||
194 | # OUTPUT_MAX=${OUTPUT_BASE}/2_max \ | ||
195 | # OUTPUT_MIN=${OUTPUT_BASE}/3_min \ | ||
196 | # openembedded-core/scripts/test-dependencies.sh --tmpdir=tmp-eglibc --targets=glib-2.0 --recipes=recipe_list --buildtype=c | ||
197 | echo "===== Compare dependencies recorded in \"${OUTPUT_MAX}\" and \"${OUTPUT_MIN}\" =====" | ||
198 | [ -n "${OUTPUTC}" ] || OUTPUTC=${OUTPUT_BASE} | ||
199 | mkdir -p ${OUTPUTC} | ||
200 | OUTPUT_FILE=${OUTPUTC}/dependency-changes | ||
201 | echo "Differences will be stored in ${OUTPUT_FILE}, dot is shown for every 100 of checked packages" | ||
202 | echo > ${OUTPUT_FILE} | ||
203 | |||
204 | [ -d ${OUTPUT_MAX} ] || echo_error "Directory with output from build 2 \"${OUTPUT_MAX}\" does not exist" | ||
205 | [ -d ${OUTPUT_MIN} ] || echo_error "Directory with output from build 3 \"${OUTPUT_MIN}\" does not exist" | ||
206 | [ -d ${OUTPUT_MAX}/packages/ ] || echo_error "Directory with packages from build 2 \"${OUTPUT_MAX}/packages/\" does not exist" | ||
207 | [ -d ${OUTPUT_MIN}/packages/ ] || echo_error "Directory with packages from build 3 \"${OUTPUT_MIN}/packages/\" does not exist" | ||
208 | i=0 | ||
209 | find ${OUTPUT_MAX}/packages/ -name latest | sed "s#${OUTPUT_MAX}/##g" | while read pkg; do | ||
210 | max_pkg=${OUTPUT_MAX}/${pkg} | ||
211 | min_pkg=${OUTPUT_MIN}/${pkg} | ||
212 | if [ ! -f "${min_pkg}" ] ; then | ||
213 | echo "ERROR: ${min_pkg} doesn't exist" | tee -a ${OUTPUT_FILE} | ||
214 | continue | ||
215 | fi | ||
216 | # strip version information in parenthesis | ||
217 | max_deps=`grep "^RDEPENDS = " ${max_pkg} | sed 's/^RDEPENDS = / /g; s/$/ /g; s/([^(]*)//g'` | ||
218 | min_deps=`grep "^RDEPENDS = " ${min_pkg} | sed 's/^RDEPENDS = / /g; s/$/ /g; s/([^(]*)//g'` | ||
219 | if [ "$i" = 100 ] ; then | ||
220 | echo -n "." # cheap progressbar | ||
221 | i=0 | ||
222 | fi | ||
223 | if [ "${max_deps}" = "${min_deps}" ] ; then | ||
224 | # it's annoying long, but at least it's showing some progress, warnings are grepped at the end | ||
225 | echo "NOTE: ${pkg} dependencies weren't changed" >> ${OUTPUT_FILE} | ||
226 | else | ||
227 | missing_deps= | ||
228 | for dep in ${max_deps}; do | ||
229 | echo "${min_deps}" | grep -q " ${dep} " || missing_deps="${missing_deps} ${dep}" | ||
230 | done | ||
231 | if [ -n "${missing_deps}" ] ; then | ||
232 | echo # to get rid of dots on last line | ||
233 | echo "WARN: ${pkg} lost dependency on ${missing_deps}" | tee -a ${OUTPUT_FILE} | ||
234 | fi | ||
235 | fi | ||
236 | i=`expr $i + 1` | ||
237 | done | ||
238 | echo # to get rid of dots on last line | ||
239 | echo "Found differences: " | ||
240 | grep "^WARN: " ${OUTPUT_FILE} | tee ${OUTPUT_FILE}.warn | ||
241 | echo "Found errors: " | ||
242 | grep "^ERROR: " ${OUTPUT_FILE} | tee ${OUTPUT_FILE}.error | ||
243 | # useful for reexecuting this script with only small subset of recipes with known issues | ||
244 | sed 's#.*[ /]packages/\([^/]*\)/\([^/]*\)/.*#\2#g' ${OUTPUT_FILE}.warn ${OUTPUT_FILE}.error | sort -u >> ${OUTPUT_BASE}/failed.recipes | ||
245 | } | ||
246 | |||
247 | for TYPE in $buildtype; do | ||
248 | case ${TYPE} in | ||
249 | 1) build_all;; | ||
250 | 2) build_every_recipe;; | ||
251 | 3) build_every_recipe;; | ||
252 | 4) build_every_recipe;; | ||
253 | c) compare_deps;; | ||
254 | *) echo_error "Invalid buildtype \"$TYPE\"" | ||
255 | esac | ||
256 | done | ||