cookutils annotate modules/compressor @ rev 1078
cook: allow multi-set & no-src receipts (sources may be downloaded inside compile_rules() individually for each set), allow change SHORT_DESC & WEB_SITE in genpkg_rules() (for differents sets);
lighttpd/cooker.css: add style for log line "Switching to the set...";
lighttpd/index.cgi: remove false-positive coloring with word "fatal", mark line "Switching to the set...", don't hide "Logs" button (fresh log initially empty and it may lasts for many seconds due to buffered "lazy write";
modules/compressor: better remove unwanted Perl files;
modules/mk_pkg_receipt: follow changed SHORT_DESC & WEB_SITE, improve condition speed (from 10 sec to 0.01 sec) when SPLIT is relatively long - looks like ${A/ $B /} is slow.
lighttpd/cooker.css: add style for log line "Switching to the set...";
lighttpd/index.cgi: remove false-positive coloring with word "fatal", mark line "Switching to the set...", don't hide "Logs" button (fresh log initially empty and it may lasts for many seconds due to buffered "lazy write";
modules/compressor: better remove unwanted Perl files;
modules/mk_pkg_receipt: follow changed SHORT_DESC & WEB_SITE, improve condition speed (from 10 sec to 0.01 sec) when SPLIT is relatively long - looks like ${A/ $B /} is slow.
author | Aleksej Bobylev <al.bobylev@gmail.com> |
---|---|
date | Thu Jun 21 12:57:52 2018 +0300 (2018-06-21) |
parents | 23700c523fdb |
children | 33dbe879cc21 |
rev | line source |
---|---|
al@865 | 1 #!/bin/sh |
al@865 | 2 # |
al@865 | 3 # compressor - module of the SliTaz Cook |
al@865 | 4 # Copyright (C) SliTaz GNU/Linux - GNU GPL v3 |
al@865 | 5 # |
al@865 | 6 |
al@865 | 7 . /usr/lib/slitaz/libcook.sh |
al@865 | 8 |
al@868 | 9 # Compressor cache stuff |
al@868 | 10 |
al@873 | 11 comp_cache_root='/home/slitaz/cache/cook' |
al@869 | 12 mkdir -p "$comp_cache_root" |
al@868 | 13 cache_stat=$(mktemp) |
al@868 | 14 |
al@868 | 15 # Cache notes. |
al@868 | 16 # Do not do the same job twice. Getting the file from the cache is much faster |
al@868 | 17 # than compressing the file one more time. In addition, this cache is trying not |
paul@1006 | 18 # to take extra space using the hardlinks. Although the files from the cache |
paul@870 | 19 # without reference to itself should be removed periodically. |
al@868 | 20 |
al@868 | 21 |
al@868 | 22 |
al@868 | 23 |
al@865 | 24 # |
al@865 | 25 # Functions |
al@865 | 26 # |
al@865 | 27 |
al@865 | 28 |
al@912 | 29 # tazpkg install command |
al@912 | 30 |
al@912 | 31 tpi() { |
al@912 | 32 tazpkg -gi --quiet --local --cookmode $1 |
al@912 | 33 } |
al@912 | 34 |
al@912 | 35 |
al@881 | 36 # Working with time (with hundredths precision) |
al@865 | 37 |
al@881 | 38 get_time() { |
al@881 | 39 cut -d" " -f2 /proc/uptime |
al@881 | 40 } |
al@865 | 41 |
al@881 | 42 calc_time() { |
al@881 | 43 # L10n: 's' is for seconds, 'm' is for minutes |
al@881 | 44 awk -va="$1" -vb="$(get_time)" -vs="$(_ 's')" -vm="$(_ 'm')" ' |
al@881 | 45 BEGIN{ |
al@881 | 46 time = b - a; |
al@881 | 47 if (time < 30) |
al@881 | 48 printf("%.2f%s\n", time, s); |
al@881 | 49 else |
al@881 | 50 printf("%.2f%s ~ %.0f%s\n", time, s, time / 60, m); |
al@881 | 51 }' |
al@865 | 52 } |
al@865 | 53 |
al@865 | 54 |
al@865 | 55 # Compressor mini summary |
al@865 | 56 |
al@865 | 57 comp_summary() { |
al@998 | 58 # "$time0" "$size0" "$size1" "$log_file" |
al@865 | 59 status |
al@865 | 60 [ "$2" -eq 0 ] && return |
al@881 | 61 saving=$(awk -va="$2" -vb="$3" 'BEGIN{ printf("%.0f\n", (a - b) / 1024) }') |
al@868 | 62 cache_msg='' |
al@868 | 63 if [ -s "$cache_stat" ]; then |
al@868 | 64 cache_msg=$(_n ' Cache hit: %d/%d.' "$(fgrep '+' $cache_stat | wc -l)" "$(wc -l < $cache_stat)") |
al@868 | 65 echo -n > $cache_stat |
al@868 | 66 fi |
al@868 | 67 _ ' Time: %s. Size: %s B -> %s B. Save: %s KB.%s' \ |
al@881 | 68 "$(calc_time $1)" "$2" "$3" "$saving" "$cache_msg" |
al@998 | 69 |
al@998 | 70 if [ -s "$4" ]; then |
al@1024 | 71 _ 'Compressor warnings and errors:' |
al@998 | 72 awk '{printf " %s\n", $0;}' "$4" |
al@998 | 73 echo |
al@998 | 74 fi |
al@998 | 75 # Clean log |
al@998 | 76 [ ! -f "$4" ] || rm "$4" |
al@865 | 77 } |
al@865 | 78 |
al@865 | 79 |
al@1020 | 80 # Find ELF files |
al@1020 | 81 |
al@1020 | 82 find_elf() { |
al@1020 | 83 local i ifs="$IFS" |
al@1020 | 84 IFS=$'\n' |
al@1020 | 85 find $fs -type f \ |
al@1020 | 86 | while read i; do |
al@1020 | 87 # output of `readelf -h <file> is human-readable information, |
paul@1021 | 88 # we are interested in the next line: |
al@1020 | 89 # Type: EXEC (Executable file) |
al@1020 | 90 # or |
al@1020 | 91 # Type: DYN (Shared object file) |
al@1020 | 92 if [ "$(readelf -h "$i" 2>/dev/null \ |
al@1020 | 93 | sed -n '/Type:/ s|.*: *\([^ ]*\) .*|\1|p')" == "$1" ]; then |
al@1020 | 94 echo "$i" # $1 = { EXEC | DYN } |
al@1020 | 95 fi |
al@1020 | 96 done |
al@1020 | 97 IFS="$ifs" |
al@1020 | 98 } |
al@1020 | 99 |
al@1020 | 100 |
al@865 | 101 # Calculating different sizes |
al@865 | 102 |
al@865 | 103 sizes() { |
al@1020 | 104 local ifs="$IFS"; IFS=$'\n' |
al@865 | 105 case $1 in |
al@865 | 106 man) find $install/usr/share/man -type f -exec ls -l \{\} \; ;; |
al@865 | 107 png) find $install -type f -name '*.png' -exec ls -l \{\} \; ;; |
al@865 | 108 svg) find $install -type f -name '*.svg' -exec ls -l \{\} \; ;; |
al@1064 | 109 gif) find $install -type f -name '*.gif' -exec ls -l \{\} \; ;; |
al@865 | 110 xml) find $install -type f \( -name '*.ui' -o -name '*.glade' \) -exec ls -l \{\} \; ;; |
al@865 | 111 des) find $install -type f -name '*.desktop' -exec ls -l \{\} \; ;; |
al@865 | 112 mo1) find $install -type f -name '*.mo' -exec ls -l \{\} \; ;; |
al@865 | 113 loc) find $install/usr/share/i18n/locales -type f -exec ls -l \{\} \; ;; |
al@865 | 114 mo2) find $fs/usr/share/locale -type f -name '*.mo' -exec ls -l \{\} \; ;; |
al@872 | 115 gz) find $install -type f -name '*.gz' ! -path '*/share/man/*' -exec ls -l \{\} \; ;; |
al@954 | 116 zip) find $install -type f -name '*.zip' -exec ls -l \{\} \; ;; |
al@1020 | 117 strip) |
al@1020 | 118 { |
al@1020 | 119 find_elf EXEC |
al@1020 | 120 find_elf DYN |
al@1020 | 121 find $fs -type f \( -name '*.a' -o -name '*.pyc' -o -name '*.pyo' \ |
al@1020 | 122 -o -name '.packlist' -o -name '*.pm' -o -name '*.pl' -o -name '*.pod' \) |
al@1020 | 123 } \ |
al@1034 | 124 | tr '\n' '\0' \ |
al@1034 | 125 | xargs -0 ls -l |
al@1020 | 126 ;; |
al@865 | 127 esac | awk '{s+=$5}END{print s}' |
al@1020 | 128 IFS="$ifs" |
al@865 | 129 } |
al@865 | 130 |
al@865 | 131 |
al@868 | 132 # Query cache for already existing compressed file; substitute original file with the cached |
al@868 | 133 # compressed one if so. |
al@868 | 134 # $1: cache section (gz, mangz, png, etc.); $2: path to original file |
al@868 | 135 |
al@868 | 136 query_cache() { |
al@868 | 137 md5=$(md5sum "$2") |
al@868 | 138 cachefile="$comp_cache_root/$1/${md5%% *}" |
al@868 | 139 echo "$cachefile" |
al@868 | 140 if [ -f "$cachefile" ]; then |
al@868 | 141 ln -f "$cachefile" "$2" |
al@868 | 142 echo '+' >> "$cache_stat" |
al@868 | 143 else |
al@868 | 144 echo '-' >> "$cache_stat" |
al@868 | 145 false |
al@868 | 146 fi |
al@868 | 147 } |
al@868 | 148 |
al@868 | 149 |
al@868 | 150 # Store compressed file to the cache |
al@868 | 151 # $1: path to cache entry to be stored; $2: path to compressed file to be stored |
al@868 | 152 |
al@868 | 153 store_cache() { |
al@868 | 154 mkdir -p "${1%/*}" |
al@868 | 155 mv "$2" "$1" |
al@868 | 156 ln "$1" "$2" |
al@868 | 157 } |
al@868 | 158 |
al@868 | 159 |
al@865 | 160 # Function to compress all man pages |
al@865 | 161 # Compressing can be disabled with COOKOPTS="!manz" |
al@865 | 162 |
al@865 | 163 compress_manpages() { |
al@881 | 164 time0=$(get_time) |
al@865 | 165 [ "${COOKOPTS/!manz/}" != "$COOKOPTS" ] && return |
al@874 | 166 manpath="$install/usr/share/man" |
al@865 | 167 [ -d "$manpath" ] || return |
al@865 | 168 size0=$(sizes man); [ -z "$size0" ] && return |
al@865 | 169 |
al@936 | 170 tpi advancecomp-static |
al@867 | 171 |
al@865 | 172 action 'Compressing man pages...' |
al@865 | 173 |
al@865 | 174 # We'll use only Gzip compression, so decompress other formats first |
al@865 | 175 find $manpath -type f -name '*.bz2' -exec bunzip2 \{\} \; |
al@865 | 176 find $manpath -type f -name '*.xz' -exec unxz \{\} \; |
al@865 | 177 |
al@865 | 178 # Fast compress with gzip |
al@874 | 179 find $manpath -type f ! -name '*.gz' -exec gzip \{\} \; |
al@865 | 180 |
al@865 | 181 # Fix symlinks |
al@874 | 182 for i in $(find $manpath -type l); do |
al@865 | 183 dest=$(readlink $i | sed 's|\.[gbx]z2*$||') |
al@865 | 184 link=$(echo $i | sed 's|\.[gbx]z2*$||') |
al@865 | 185 rm $i; ln -s $dest.gz $link.gz |
al@865 | 186 done |
al@865 | 187 |
al@865 | 188 # Recompress with advdef (it can't compress, only recompress) |
al@1024 | 189 the_log="$(mktemp)" |
al@1024 | 190 if which advdef >/dev/null; then |
al@1024 | 191 IFS=$'\n' |
al@1024 | 192 for i in $(find $manpath -type f); do |
al@1024 | 193 if ! cached_path=$(query_cache mangz "$i"); then |
al@1024 | 194 cp -a "$i" "$i.orig$$" # save the original if something goes wrong |
al@1024 | 195 out="$(advdef -z4q "$i")" |
al@1024 | 196 if [ -n "$out" ]; then |
al@1024 | 197 echo "$i:"$'\n'"$out"$'\n' >> "$the_log" |
al@1024 | 198 mv -f "$i.orig$$" "$i" # restore the original |
al@1024 | 199 else |
al@1024 | 200 store_cache "$cached_path" "$i" |
al@1024 | 201 rm -f "$i.orig$$" # clean |
al@1024 | 202 fi |
al@998 | 203 fi |
al@1024 | 204 done |
al@1024 | 205 else |
al@1024 | 206 echo 'Warning: advdef not found.' > "$the_log" |
al@1024 | 207 fi |
al@865 | 208 |
al@1024 | 209 comp_summary "$time0" "$size0" "$(sizes man)" "$the_log" |
al@865 | 210 } |
al@865 | 211 |
al@865 | 212 |
al@872 | 213 # Function to recompress all gzip archives |
al@872 | 214 # Recompressing can be disabled with COOKOPTS="!gz" |
al@872 | 215 |
al@872 | 216 recompress_gz() { |
al@881 | 217 time0=$(get_time) |
al@872 | 218 [ "${COOKOPTS/!gz/}" != "$COOKOPTS" ] && return |
al@872 | 219 size0=$(sizes gz); [ -z "$size0" ] && return |
al@872 | 220 |
al@936 | 221 tpi advancecomp-static |
al@872 | 222 |
al@872 | 223 action 'Recompressing gzip files...' |
al@872 | 224 |
al@872 | 225 # Recompress with advdef |
al@1024 | 226 the_log="$(mktemp)" |
al@1024 | 227 if which advdef >/dev/null; then |
al@1024 | 228 IFS=$'\n' |
al@1024 | 229 for i in $(find $install -type f -name '*.gz' ! -path '*/share/man/*'); do |
al@1024 | 230 if ! cached_path=$(query_cache gz "$i"); then |
al@1024 | 231 cp -a "$i" "$i.orig$$" # save the original if something goes wrong |
al@1024 | 232 out="$(advdef -z4q "$i")" |
al@1024 | 233 if [ -n "$out" ]; then |
al@1024 | 234 echo "$i:"$'\n'"$out"$'\n' >> "$the_log" |
al@1024 | 235 mv -f "$i.orig$$" "$i" # restore the original |
al@1024 | 236 else |
al@1024 | 237 store_cache "$cached_path" "$i" |
al@1024 | 238 rm -f "$i.orig$$" # clean |
al@1024 | 239 fi |
al@998 | 240 fi |
al@1024 | 241 done |
al@1024 | 242 else |
al@1024 | 243 echo 'Warning: advdef not found.' > "$the_log" |
al@1024 | 244 fi |
al@872 | 245 |
al@1024 | 246 comp_summary "$time0" "$size0" "$(sizes gz)" "$the_log" |
al@872 | 247 } |
al@872 | 248 |
al@872 | 249 |
al@954 | 250 # Function to recompress all zip archives |
al@954 | 251 # Recompressing can be disabled with COOKOPTS="!zip" |
al@954 | 252 |
al@954 | 253 recompress_zip() { |
al@954 | 254 time0=$(get_time) |
al@954 | 255 [ "${COOKOPTS/!zip/}" != "$COOKOPTS" ] && return |
al@954 | 256 size0=$(sizes zip); [ -z "$size0" ] && return |
al@954 | 257 |
al@954 | 258 tpi advancecomp-static |
al@954 | 259 |
al@954 | 260 action 'Recompressing zip files...' |
al@954 | 261 |
al@954 | 262 # Recompress with advzip |
al@1024 | 263 the_log="$(mktemp)" |
al@1024 | 264 if which advzip >/dev/null; then |
al@1024 | 265 IFS=$'\n' |
al@1024 | 266 for i in $(find $install -type f -name '*.zip'); do |
al@1024 | 267 if ! cached_path=$(query_cache zip "$i"); then |
al@1024 | 268 cp -a "$i" "$i.orig$$" # save the original if something goes wrong |
al@1024 | 269 out="$(advzip -z3qk "$i")" # '-4' is more than two orders slower; use '-3' |
al@1024 | 270 if [ -n "$out" ]; then |
al@1024 | 271 echo "$i:"$'\n'"$out"$'\n' >> "$the_log" |
al@1024 | 272 mv -f "$i.orig$$" "$i" # restore the original |
al@1024 | 273 else |
al@1024 | 274 store_cache "$cached_path" "$i" |
al@1024 | 275 rm -f "$i.orig$$" # clean |
al@1024 | 276 fi |
al@998 | 277 fi |
al@1024 | 278 done |
al@1024 | 279 else |
al@1024 | 280 echo 'Warning: advzip not found.' > "$the_log" |
al@1024 | 281 fi |
al@954 | 282 |
al@1024 | 283 comp_summary "$time0" "$size0" "$(sizes zip)" "$the_log" |
al@954 | 284 } |
al@954 | 285 |
al@954 | 286 |
al@865 | 287 # Function used after compile_rules() to compress all png images |
al@865 | 288 # Compressing can be disabled with COOKOPTS="!pngz" |
al@865 | 289 |
al@865 | 290 compress_png() { |
al@881 | 291 time0=$(get_time) |
al@865 | 292 [ "${COOKOPTS/!pngz/}" != "$COOKOPTS" ] && return |
al@865 | 293 size0=$(sizes png); [ -z "$size0" ] && return |
al@865 | 294 |
al@865 | 295 use_pq=true |
al@865 | 296 use_op=true |
al@865 | 297 [ "${COOKOPTS/!pngquant/}" != "$COOKOPTS" ] && use_pq=false |
al@865 | 298 [ "${COOKOPTS/!optipng/}" != "$COOKOPTS" ] && use_op=false |
al@932 | 299 $use_pq && tpi pngquant-static |
al@932 | 300 $use_op && tpi optipng-static |
al@865 | 301 |
al@867 | 302 action 'Compressing png images...' |
al@865 | 303 |
al@1024 | 304 the_log="$(mktemp)" |
al@1024 | 305 $use_pq && if ! which pngquant >/dev/null; then |
al@1024 | 306 echo 'Warning: pngquant not found.' > "$the_log" |
al@1024 | 307 use_pq=false |
al@1024 | 308 fi |
al@1024 | 309 $use_op && if ! which optipng >/dev/null; then |
al@1024 | 310 echo 'Warning: optipng not found.' >> "$the_log" |
al@1024 | 311 use_op=false |
al@1024 | 312 fi |
al@1024 | 313 |
al@865 | 314 oplevel=$(echo $COOKOPTS | grep 'op[0-8]' | sed 's|.*op\([0-8]\).*|\1|') |
al@868 | 315 [ -z "$oplevel" ] && oplevel='2' |
al@868 | 316 |
al@868 | 317 cache_section="png$oplevel" |
al@868 | 318 $use_pq && cache_section="${cache_section}p" |
al@868 | 319 $use_op && cache_section="${cache_section}o" |
al@868 | 320 |
al@865 | 321 [ "$oplevel" == '8' ] && oplevel='7 -zm1-9' |
al@865 | 322 |
al@998 | 323 pq_opt='--skip-if-larger' # Sublime Text is mad about `if` in $(), so put it separately |
al@978 | 324 IFS=$'\n' |
al@978 | 325 for i in $(find $install -type f -name '*.png'); do |
al@998 | 326 unset IFS iserror |
al@868 | 327 if ! cached_path=$(query_cache $cache_section "$i"); then |
al@998 | 328 cp -a "$i" "$i.orig$$" # save the original if something goes wrong |
al@998 | 329 if $use_pq; then |
al@998 | 330 out="$(pngquant -f $pq_opt --ext .png --speed 1 "$i" 2>&1)" |
al@998 | 331 if [ -n "$out" ]; then |
al@1024 | 332 echo "$i (pngquant):"$'\n'"$out"$'\n' >> "$the_log" |
al@998 | 333 iserror='yes' |
al@1045 | 334 [ -e "$i.tmp" ] && rm "$i.tmp" # zero-size file remains on pngquant fail |
al@998 | 335 fi |
al@998 | 336 fi |
al@998 | 337 if $use_op && [ -z "$iserror" ]; then |
al@998 | 338 out="$(optipng -quiet -strip all -o$oplevel "$i" 2>&1)" |
al@998 | 339 if [ -n "$out" ]; then |
al@1024 | 340 echo "$i (optipng):"$'\n'"$out"$'\n' >> "$the_log" |
al@998 | 341 iserror='yes' |
al@998 | 342 fi |
al@998 | 343 fi |
al@998 | 344 if [ -n "$iserror" ]; then |
al@998 | 345 mv -f "$i.orig$$" "$i" # restore the original |
al@998 | 346 else |
al@998 | 347 store_cache "$cached_path" "$i" |
al@998 | 348 rm -f "$i.orig$$" # clean |
al@998 | 349 fi |
al@868 | 350 fi |
al@865 | 351 done |
al@865 | 352 |
al@1024 | 353 comp_summary "$time0" "$size0" "$(sizes png)" "$the_log" |
al@865 | 354 } |
al@865 | 355 |
al@865 | 356 |
al@865 | 357 # Function used after compile_rules() to compress all svg images |
al@865 | 358 # Compressing can be disabled with COOKOPTS="!svgz" |
al@865 | 359 |
al@865 | 360 compress_svg() { |
al@881 | 361 time0=$(get_time) |
al@865 | 362 [ "${COOKOPTS/!svgz/}" != "$COOKOPTS" ] && return |
al@865 | 363 size0=$(sizes svg); [ -z "$size0" ] && return |
al@865 | 364 |
al@912 | 365 tpi svgcleaner |
al@867 | 366 |
al@865 | 367 action 'Compressing svg images...' |
al@865 | 368 |
al@1072 | 369 the_log="$(mktemp)" |
al@1024 | 370 if which svgcleaner >/dev/null; then |
al@1024 | 371 [ "${COOKOPTS/!svgextra/}" == "$COOKOPTS" ] && |
al@1024 | 372 opts="--apply-transform-to-paths yes --coordinates-precision 1 --paths-coordinates-precision 1" |
al@959 | 373 |
al@1024 | 374 for i in $(IFS=$'\n' find $install -type f -name '*.svg'); do |
al@1024 | 375 out="$(unset IFS; svgcleaner "$i" "$i" --copy-on-error --quiet \ |
al@1024 | 376 --multipass --remove-unresolved-classes no $opts 2>&1)" |
al@1024 | 377 [ -z "$out" ] || echo "$i:"$'\n'"$out"$'\n' >> "$the_log" |
al@1024 | 378 done |
al@1024 | 379 else |
al@1024 | 380 echo 'Warning: svgcleaner not found.' > "$the_log" |
al@1024 | 381 fi |
al@865 | 382 |
al@1024 | 383 comp_summary "$time0" "$size0" "$(sizes svg)" "$the_log" |
al@865 | 384 } |
al@865 | 385 |
al@865 | 386 |
al@1064 | 387 # Function used after compile_rules() to compress all gif images |
al@1064 | 388 # Compressing can be disabled with COOKOPTS="!gifz" |
al@1064 | 389 |
al@1064 | 390 compress_gif() { |
al@1064 | 391 time0=$(get_time) |
al@1064 | 392 [ "${COOKOPTS/!gifz/}" != "$COOKOPTS" ] && return |
al@1064 | 393 size0=$(sizes gif); [ -z "$size0" ] && return |
al@1064 | 394 |
al@1064 | 395 tpi gifsicle |
al@1064 | 396 |
al@1064 | 397 action 'Compressing gif images...' |
al@1064 | 398 |
al@1072 | 399 the_log="$(mktemp)" |
al@1064 | 400 if which gifsicle >/dev/null; then |
al@1067 | 401 IFS=$'\n' |
al@1067 | 402 for i in $(find $install -type f -name '*.gif'); do |
al@1072 | 403 if ! cached_path=$(query_cache gif "$i"); then |
al@1072 | 404 unset IFS |
al@1072 | 405 # use intermediate file, if all well ($?=0), then substitute the original |
al@1072 | 406 if gifsicle -O3 "$i" -o "$i.$$" >> "$the_log" 2>&1; then |
al@1072 | 407 if [ -s "$i.$$" ]; then |
al@1072 | 408 mv "$i.$$" "$i" |
al@1072 | 409 store_cache "$cached_path" "$i" |
al@1072 | 410 fi |
al@1072 | 411 else |
al@1072 | 412 rm "$i.$$" |
al@1072 | 413 fi |
al@1064 | 414 fi |
al@1064 | 415 done |
al@1064 | 416 else |
al@1064 | 417 echo 'Warning: gifsicle not found.' > "$the_log" |
al@1064 | 418 fi |
al@1064 | 419 |
al@1064 | 420 comp_summary "$time0" "$size0" "$(sizes gif)" "$the_log" |
al@1064 | 421 } |
al@1064 | 422 |
al@1064 | 423 |
al@865 | 424 # Function used after compile_rules() to shrink all *.ui and *.glade files: |
al@865 | 425 # remove insignificant spaces and comments |
al@865 | 426 # Compressing can be disabled with COOKOPTS="!uiz" |
al@865 | 427 |
al@865 | 428 compress_ui() { |
al@865 | 429 [ "${COOKOPTS/!uiz/}" != "$COOKOPTS" ] && return |
al@865 | 430 [ -z "$(find $install -type f \( -name '*.ui' -o -name '*.glade' \) )" ] && return |
al@865 | 431 |
al@912 | 432 tpi xmlstarlet |
al@867 | 433 |
al@865 | 434 action 'Compressing ui files...' |
al@867 | 435 |
al@1072 | 436 the_log="$(mktemp)" |
al@1024 | 437 if which xmlstarlet >/dev/null; then |
al@1024 | 438 size0=$(sizes xml) |
al@1024 | 439 time0=$(get_time) |
al@1024 | 440 temp_ui="$(mktemp)" |
al@1024 | 441 IFS=$'\n' |
al@1024 | 442 for ui in $(find $install -type f \( -name '*.ui' -o -name '*.glade' \) ); do |
al@1024 | 443 out="$(xmlstarlet c14n --without-comments "$ui" | xmlstarlet sel -B -t -c '*' > "$temp_ui")" |
al@1024 | 444 if [ -n "$out" ]; then |
al@1024 | 445 echo "$ui:"$'\n'"$out"$'\n' >> "$the_log" |
al@1024 | 446 else |
al@1024 | 447 cat "$temp_ui" > "$ui" |
al@1024 | 448 fi |
al@1024 | 449 done |
al@1024 | 450 else |
al@1024 | 451 echo 'Warning: xmlstarlet not found.' > "$the_log" |
al@1024 | 452 fi |
al@865 | 453 |
al@1024 | 454 comp_summary "$time0" "$size0" "$(sizes xml)" "$the_log" |
al@865 | 455 rm "$temp_ui" |
al@865 | 456 } |
al@865 | 457 |
al@865 | 458 |
al@865 | 459 # Get list of supported locales... |
al@865 | 460 |
al@865 | 461 get_supported_locales() { |
al@865 | 462 lpc='/slitaz-i18n/stuff/locale-pack.conf' |
al@865 | 463 if [ -e "$WOK$lpc" ]; then |
al@865 | 464 # ... from package in the local wok |
al@865 | 465 . "$WOK$lpc" |
al@865 | 466 else |
al@865 | 467 # ... from Hg |
al@865 | 468 temp_conf=$(mktemp) |
al@865 | 469 wget -q -O $temp_conf -T 10 "http://hg.slitaz.org/wok/raw-file/tip$lpc" |
al@865 | 470 if [ -s $temp_conf ]; then |
al@865 | 471 . $temp_conf |
al@865 | 472 else |
al@865 | 473 # Give up and use hardcoded list |
al@865 | 474 LOCALE_PACK="ar ca cs da de el en es fi fr hr hu id is it ja nb nl nn pl pt \ |
al@865 | 475 pt_BR ro ru sl sv tr uk zh_CN zh_TW" |
al@865 | 476 fi |
al@865 | 477 rm $temp_conf |
al@865 | 478 fi |
al@865 | 479 echo $LOCALE_PACK |
al@865 | 480 } |
al@865 | 481 |
al@865 | 482 |
al@865 | 483 # Fix common errors and warnings in the .desktop files |
al@865 | 484 # Fixing can be disabled with COOKOPTS="!fixdesktops" |
al@865 | 485 |
al@865 | 486 fix_desktop_files() { |
al@865 | 487 [ "${COOKOPTS/!fixdesktops/}" != "$COOKOPTS" ] && return |
al@876 | 488 deskpath="$install/usr/share/applications" |
al@876 | 489 [ -d "$deskpath" ] || return |
al@876 | 490 [ -z "$(find $deskpath -type f -name '*.desktop')" ] && return |
al@865 | 491 |
al@865 | 492 size0=$(sizes des) |
al@881 | 493 time0=$(get_time) |
al@865 | 494 |
al@865 | 495 if [ -n "$QA" -a -z "$(which desktop-file-validate)" ]; then |
al@936 | 496 tpi desktop-file-validate-static |
al@865 | 497 fi |
al@865 | 498 |
al@865 | 499 # The variable $LOCALE is set in cook.conf and may be overridden in the receipt. |
al@865 | 500 # Default value is "" (empty). That means for us that we'll use the full |
al@865 | 501 # list of supported locales here. |
al@865 | 502 [ -z "$LOCALE" ] && LOCALE=$(get_supported_locales) |
al@865 | 503 |
al@876 | 504 IFS=$'\n' |
al@876 | 505 for desktop in $(find $deskpath -type f -name '*.desktop'); do |
al@865 | 506 cp "$desktop" "$desktop.orig" |
al@865 | 507 |
al@865 | 508 # Sort out .desktop file (is prerequisite to correct working of `fix-desktop-file`) |
al@865 | 509 sdft "$desktop" -i |
al@865 | 510 |
al@865 | 511 # Fix common errors in .desktop file |
al@865 | 512 fix-desktop-file "$desktop" |
al@865 | 513 |
al@865 | 514 # Strip unsupported locales from .desktop file |
al@865 | 515 [ "${COOKOPTS/!i18nz/}" == "$COOKOPTS" ] && |
al@865 | 516 sdft "$desktop" -i -k "$LOCALE" |
al@865 | 517 |
al@865 | 518 # Extra-strip |
al@865 | 519 [ "${COOKOPTS/!extradesktops/}" == "$COOKOPTS" ] && |
al@865 | 520 sdft "$desktop" -i -g -x -tf -r 'Keywords*' -o |
al@865 | 521 |
al@865 | 522 if [ -n "$QA" ]; then |
al@865 | 523 # Check the rest of errors, warnings and tips |
al@865 | 524 _ 'QA: Checking %s...' "$(basename $desktop)" |
al@1002 | 525 busybox diff "$desktop.orig" "$desktop" | sed 's!^!|!' |
al@1036 | 526 if which desktop-file-validate >/dev/null; then |
al@1024 | 527 desktop-file-validate "$desktop" | busybox fold -s |
al@1024 | 528 else |
al@1024 | 529 echo 'Warning: desktop-file-validate not found.' |
al@1024 | 530 fi |
al@865 | 531 echo |
al@865 | 532 fi |
al@865 | 533 |
al@865 | 534 rm "$desktop.orig" |
al@865 | 535 done |
al@865 | 536 |
al@998 | 537 comp_summary "$time0" "$size0" "$(sizes des)" '/dev/null' |
al@865 | 538 } |
al@865 | 539 |
al@865 | 540 |
paul@879 | 541 # Normalize all *.mo files: unconditionally convert to UTF-8; remove strings that are not really necessary |
al@865 | 542 # to the translation (msgid = msgstr) |
al@865 | 543 # Normalization can be disabled with COOKOPTS="!monorm" |
al@865 | 544 |
al@865 | 545 normalize_mo() { |
al@865 | 546 [ "${COOKOPTS/!monorm/}" != "$COOKOPTS" ] && return |
al@865 | 547 [ -z "$(find $install -type f -name '*.mo')" ] && return |
al@865 | 548 |
al@867 | 549 # Gettext functions: msgunfmt, msguniq, msgconv, msgfmt |
al@912 | 550 tpi gettext |
al@867 | 551 # Gconv modules (convert to UTF-8) |
al@912 | 552 tpi glibc-locale |
al@867 | 553 |
al@865 | 554 action 'Normalizing mo files...' |
al@867 | 555 |
al@1024 | 556 the_log="$(mktemp)" |
al@1024 | 557 to_continue=true |
al@1024 | 558 for i in msgunfmt msguniq msgconv msgfmt; do |
al@1024 | 559 if ! which $i >/dev/null; then |
al@1024 | 560 echo "Warning: $i not found. Normalizing aborted" > "$the_log" |
al@1024 | 561 to_continue=false |
al@1024 | 562 fi |
al@1024 | 563 done |
al@1024 | 564 |
al@865 | 565 size0=$(sizes mo1) |
al@881 | 566 time0=$(get_time) |
al@865 | 567 |
al@865 | 568 # Process all existing *.mo files |
al@876 | 569 IFS=$'\n' |
al@1024 | 570 $to_continue && |
al@865 | 571 for mo in $(find "$install" -type f -name '*.mo'); do |
al@865 | 572 tmpfile="$(mktemp)" |
al@865 | 573 |
al@998 | 574 # put ANY errors of {msgunfmt,msguniq,msgconv} to $out. FIXME? |
al@998 | 575 out="$({ msgunfmt "$mo" | msguniq | msgconv -o "$tmpfile" -t 'UTF-8'; } 2>&1)" |
al@998 | 576 if [ -n "$out" ]; then |
al@998 | 577 # using literal $'\n' here instead of using `echo -e "...\n..."` because |
al@998 | 578 # $out may contain escapes ('\r', '\v') that we should print as-is |
al@1024 | 579 echo "$mo:"$'\n'"$out"$'\n' >> "$the_log" |
al@998 | 580 continue # proceed to next file |
al@998 | 581 fi |
al@998 | 582 |
al@865 | 583 # add newline |
al@865 | 584 echo >> "$tmpfile" |
al@865 | 585 |
al@865 | 586 # get Plural-Forms |
al@865 | 587 awk ' |
al@865 | 588 BEGIN { skip = ""; } |
al@865 | 589 { |
al@865 | 590 if (! skip) { |
al@865 | 591 s = $0; |
al@865 | 592 gsub(/^[^\"]*\"/, "", s); |
al@865 | 593 gsub(/\"$/, "", s); |
al@865 | 594 printf("%s", s); |
al@865 | 595 } |
al@865 | 596 if (! $0) skip = "yes"; |
al@865 | 597 } |
al@865 | 598 ' "$tmpfile" | sed 's|\\n|\n|g' | grep "^Plural-Forms:" > "$tmpfile.pf" |
al@865 | 599 |
al@865 | 600 if ! grep -q 'msgid_plural' "$tmpfile"; then |
al@865 | 601 echo > "$tmpfile.pf" |
al@865 | 602 fi |
al@865 | 603 |
al@865 | 604 # main |
al@865 | 605 awk -v pf="$(cat "$tmpfile.pf")" ' |
al@865 | 606 function clean() { |
al@865 | 607 mode = msgctxt = msgid = msgid_plural = msgstr = msgstr0 = msgstr1 = msgstr2 = msgstr3 = msgstr4 = msgstr5 = ""; |
al@865 | 608 } |
al@865 | 609 |
al@865 | 610 function getstring() { |
al@865 | 611 # Skip unquoted words at the beginning (msgid, msgstr...) and get string from inside quotes |
al@865 | 612 s = $0; |
al@865 | 613 gsub(/^[^\"]*\"/, "", s); |
al@865 | 614 gsub(/\"$/, "", s); |
al@865 | 615 return s; |
al@865 | 616 } |
al@865 | 617 |
al@865 | 618 BEGIN { |
al@865 | 619 printf("msgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n"); |
al@865 | 620 if (pf) |
al@865 | 621 printf("\"%s\\n\"\n", pf); |
al@865 | 622 printf("\n"); |
al@865 | 623 skip = 1; |
al@865 | 624 clean(); |
al@865 | 625 } |
al@865 | 626 |
al@865 | 627 { |
al@865 | 628 # Skip the entire header |
al@865 | 629 if (!skip) { |
al@865 | 630 if ($1 == "msgctxt" || $1 == "msgid" || $1 == "msgstr" || $1 == "msgid_plural") |
al@865 | 631 mode = $1; |
al@865 | 632 if ($1 == "msgstr[0]") mode = "msgstr0"; |
al@865 | 633 if ($1 == "msgstr[1]") mode = "msgstr1"; |
al@865 | 634 if ($1 == "msgstr[2]") mode = "msgstr2"; |
al@865 | 635 if ($1 == "msgstr[3]") mode = "msgstr3"; |
al@865 | 636 if ($1 == "msgstr[4]") mode = "msgstr4"; |
al@865 | 637 if ($1 == "msgstr[5]") mode = "msgstr5"; |
al@865 | 638 |
al@865 | 639 if (mode == "msgctxt") msgctxt = msgctxt getstring(); |
al@865 | 640 if (mode == "msgid") msgid = msgid getstring(); |
al@865 | 641 if (mode == "msgstr") msgstr = msgstr getstring(); |
al@865 | 642 if (mode == "msgid_plural") msgid_plural = msgid_plural getstring(); |
al@865 | 643 if (mode == "msgstr0") msgstr0 = msgstr0 getstring(); |
al@865 | 644 if (mode == "msgstr1") msgstr1 = msgstr1 getstring(); |
al@865 | 645 if (mode == "msgstr2") msgstr2 = msgstr2 getstring(); |
al@865 | 646 if (mode == "msgstr3") msgstr3 = msgstr3 getstring(); |
al@865 | 647 if (mode == "msgstr4") msgstr4 = msgstr4 getstring(); |
al@865 | 648 if (mode == "msgstr5") msgstr5 = msgstr5 getstring(); |
al@865 | 649 |
al@865 | 650 if (! $0) { |
al@865 | 651 if (msgid != msgstr) { |
al@865 | 652 if (msgctxt) printf("msgctxt \"%s\"\n", msgctxt); |
al@865 | 653 printf("msgid \"%s\"\n", msgid); |
al@865 | 654 if (msgid_plural) printf("msgid_plural \"%s\"\n", msgid_plural); |
al@865 | 655 if (msgstr) printf("msgstr \"%s\"\n", msgstr); |
al@865 | 656 if (msgstr0) printf("msgstr[0] \"%s\"\n", msgstr0); |
al@865 | 657 if (msgstr1) printf("msgstr[1] \"%s\"\n", msgstr1); |
al@865 | 658 if (msgstr2) printf("msgstr[2] \"%s\"\n", msgstr2); |
al@865 | 659 if (msgstr3) printf("msgstr[3] \"%s\"\n", msgstr3); |
al@865 | 660 if (msgstr4) printf("msgstr[4] \"%s\"\n", msgstr4); |
al@865 | 661 if (msgstr5) printf("msgstr[5] \"%s\"\n", msgstr5); |
al@865 | 662 printf("\n"); |
al@865 | 663 } |
al@865 | 664 clean(); |
al@865 | 665 } |
al@865 | 666 } |
al@865 | 667 if ($0 == "") skip = ""; |
al@865 | 668 } |
al@865 | 669 ' "$tmpfile" > "$tmpfile.awk" |
al@865 | 670 |
al@998 | 671 out="$(msgfmt "$tmpfile.awk" -o "$tmpfile.mo" 2>&1)" |
al@998 | 672 if [ -n "$out" ]; then |
al@1024 | 673 echo "$mo (msgfmt):"$'\n'"$out"$'\n' >> "$the_log" |
al@998 | 674 continue # proceed to next file |
al@998 | 675 fi |
al@865 | 676 |
al@865 | 677 if [ -s "$tmpfile.mo" ]; then |
al@865 | 678 rm "$mo"; mv "$tmpfile.mo" "$mo" |
al@865 | 679 else |
al@1024 | 680 _ 'Error processing %s' "$mo" >> "$the_log" |
al@1024 | 681 echo >> "$the_log" |
al@865 | 682 [ -e "$tmpfile.mo" ] && rm "$tmpfile.mo" |
al@865 | 683 fi |
al@865 | 684 |
al@865 | 685 # Clean |
al@865 | 686 rm "$tmpfile" "$tmpfile.pf" "$tmpfile.awk" |
al@865 | 687 done |
al@865 | 688 |
al@1024 | 689 comp_summary "$time0" "$size0" "$(sizes mo1)" "$the_log" |
al@865 | 690 } |
al@865 | 691 |
al@865 | 692 |
al@865 | 693 # Strip locale definitions: normalize whitespace and remove comments |
al@865 | 694 # Stripping can be disabled with COOKOPTS="!locdef" |
al@865 | 695 |
al@865 | 696 strip_locale_def() { |
al@865 | 697 [ "${COOKOPTS/!locdef/}" != "$COOKOPTS" ] && return |
al@865 | 698 [ ! -d "$install/usr/share/i18n/locales" ] && return |
al@865 | 699 [ -z "$(find $install/usr/share/i18n/locales -type f)" ] && return |
al@865 | 700 |
al@865 | 701 action 'Stripping locale definitions...' |
al@865 | 702 size0=$(sizes loc) |
al@881 | 703 time0=$(get_time) |
al@865 | 704 |
al@865 | 705 for i in $(find $install/usr/share/i18n/locales -type f); do |
al@865 | 706 sed -i 's| | |g; s| *| |g; s|^ ||; /^%/d' $i |
al@865 | 707 done |
al@865 | 708 |
al@865 | 709 comp_summary "$time0" "$size0" "$(sizes loc)" |
al@865 | 710 } |
al@865 | 711 |
al@865 | 712 |
al@865 | 713 # Find and strip: --strip-all (-s) or --strip-debug on static libs as well |
al@865 | 714 # as removing unneeded files like in Python packages. Cross compiled binaries |
al@865 | 715 # must be stripped with cross-tools aka $ARCH-slitaz-*-strip |
al@865 | 716 # Stripping can be disabled with COOKOPTS="!strip" |
al@865 | 717 |
al@865 | 718 strip_package() { |
al@865 | 719 [ "${COOKOPTS/!strip/}" != "$COOKOPTS" ] && return |
al@865 | 720 |
al@1020 | 721 local i ifs="$IFS" |
al@1020 | 722 IFS=$'\n' |
al@1020 | 723 |
al@865 | 724 case "$ARCH" in |
al@865 | 725 arm*|x86_64) export STRIP="$HOST_SYSTEM-strip" ;; |
al@865 | 726 *) export STRIP='strip' ;; |
al@865 | 727 esac |
al@865 | 728 action 'Executing strip on all files...' |
al@865 | 729 size0=0 |
al@865 | 730 size1=0 |
al@881 | 731 time0=$(get_time) |
al@1020 | 732 oldsize=$(sizes strip) |
al@865 | 733 |
al@1027 | 734 |
al@1027 | 735 # GNU strip (GNU Binutils) |
al@1027 | 736 # -p --preserve-dates Copy modified/access timestamps to the output |
paul@1030 | 737 # -s --strip-all Remove all symbols and relocation information |
al@1027 | 738 # --strip-unneeded Remove all symbols not needed by relocations |
al@1027 | 739 # -D --enable-deterministic-archives Produce deterministic output when stripping archives |
al@1027 | 740 # -g -S -d --strip-debug Remove all debugging symbols & sections |
al@1027 | 741 |
al@1027 | 742 # Strip executable files |
al@1020 | 743 while read i; do |
al@1027 | 744 $STRIP -ps "$i" 2>/dev/null |
al@1020 | 745 done <<EOT |
al@1027 | 746 $(find_elf EXEC) |
al@1027 | 747 EOT |
al@1027 | 748 |
al@1027 | 749 # Strip shared libraries |
al@1027 | 750 while read i; do |
al@1027 | 751 case $i in |
al@1027 | 752 *.dbg) ;; # skip library.so.*.dbg debugging symbols |
al@1027 | 753 *) $STRIP -p --strip-unneeded "$i" 2>/dev/null;; |
al@1027 | 754 esac |
al@1027 | 755 done <<EOT |
al@1027 | 756 $(find_elf DYN) |
al@1020 | 757 EOT |
al@865 | 758 |
al@1020 | 759 # Strip static libraries |
al@1027 | 760 # See also: https://wiki.debian.org/ReproducibleBuilds/TimestampsInStaticLibraries |
al@1027 | 761 find $fs -name '*.a' -exec $STRIP -pdD '{}' 2>/dev/null \; |
al@955 | 762 |
al@899 | 763 |
al@899 | 764 # Remove Python *.pyc and *.pyo |
al@865 | 765 find $fs -type f \( -name '*.pyc' -o -name '*.pyo' \) -delete 2>/dev/null |
al@865 | 766 |
al@899 | 767 # Remove both with the empty subfolders: |
al@899 | 768 # 1. Perl perllocal.pod and .packlist (unconditionally) |
al@899 | 769 local perlfiles="$(find $fs -type f \( -name 'perllocal.pod' -o -name '.packlist' \))" |
al@899 | 770 # 2. Perl *.pod (if not disabled) |
al@899 | 771 [ "${COOKOPTS/!rmpod/}" == "$COOKOPTS" ] && |
al@1078 | 772 perlfiles="$perlfiles"$'\n'"$(find $fs -type f -name '*.pod')" |
al@1078 | 773 echo "$perlfiles" | sort -u | xargs rm -f 2>/dev/null |
al@1078 | 774 echo "$perlfiles" | sort -u | awk 'BEGIN{FS=OFS="/"}{$NF="";print}' \ |
al@1078 | 775 | xargs rmdir -p --ignore-fail-on-non-empty 2>/dev/null |
al@899 | 776 |
al@1020 | 777 # Strip documentation inside Perl files (*.pm and *.pl) (if not disabled) |
al@899 | 778 [ "${COOKOPTS/!perlz/}" == "$COOKOPTS" ] && |
al@899 | 779 find $fs -type f \( -name '*.pm' -o -name '*.pl' \) -exec sed -i '/^=/,/^=cut/d' '{}' \; |
al@899 | 780 |
al@1020 | 781 newsize=$(sizes strip) |
al@865 | 782 |
al@1020 | 783 comp_summary "$time0" "$oldsize" "$newsize" |
al@1020 | 784 IFS="$ifs" |
al@865 | 785 } |
al@865 | 786 |
al@865 | 787 |
al@865 | 788 # Strip unsupported locales (.mo files) |
al@865 | 789 |
al@865 | 790 strip_mo_i18n() { |
al@865 | 791 [ "${COOKOPTS/!i18nz/}" != "$COOKOPTS" ] && return |
al@865 | 792 |
al@865 | 793 [ ! -d "$fs/usr/share/locale" ] && return |
al@865 | 794 [ -z "$(find $fs/usr/share/locale -type f -name '*.mo')" ] && return |
al@865 | 795 |
al@865 | 796 action 'Thin out translation files...' |
al@865 | 797 size0=$(sizes mo2) |
al@881 | 798 time0=$(get_time) |
al@865 | 799 |
al@865 | 800 # The variable $LOCALE is set in cook.conf and may be overridden in the receipt. |
al@865 | 801 # Default value is "" (empty). That means for us that we'll use the full |
al@865 | 802 # list of supported locales here. |
al@865 | 803 [ -z "$LOCALE" ] && LOCALE=$(get_supported_locales) |
al@865 | 804 |
al@865 | 805 # Existing locales |
al@865 | 806 elocales=" $(ls -1 "$fs/usr/share/locale" | tr '\n' ' ') " |
al@865 | 807 |
al@865 | 808 # Thin out the list of existing locales. At the end there will be only locales that need |
al@865 | 809 # deleting (and the garbage like '_AU', _US', '_BR' leaving from 'en_AU', 'en_US', 'pt_BR'...) |
al@865 | 810 for keep_locale in $LOCALE; do |
al@865 | 811 elocales=${elocales//$keep_locale} |
al@865 | 812 done |
al@865 | 813 |
al@865 | 814 # Remove the unsupported locales |
al@865 | 815 for rem_locale in $elocales; do |
al@998 | 816 [ ! -d "$fs/usr/share/locale/$rem_locale" ] || |
al@865 | 817 rm -r "$fs/usr/share/locale/$rem_locale" |
al@865 | 818 done |
al@865 | 819 |
al@865 | 820 comp_summary "$time0" "$size0" "$(sizes mo2)" |
al@865 | 821 } |
al@865 | 822 |
al@865 | 823 |
al@865 | 824 |
al@865 | 825 |
al@865 | 826 case $1 in |
al@865 | 827 install) |
al@865 | 828 # Compressors working in the $install |
al@865 | 829 case "$ARCH" in |
al@865 | 830 arm*) ;; |
al@865 | 831 *) |
al@872 | 832 recompress_gz |
al@954 | 833 recompress_zip |
al@865 | 834 compress_manpages |
al@865 | 835 compress_png |
al@865 | 836 compress_svg |
al@1064 | 837 compress_gif |
al@865 | 838 compress_ui |
al@865 | 839 ;; |
al@865 | 840 esac |
al@865 | 841 |
al@865 | 842 fix_desktop_files |
al@865 | 843 normalize_mo |
al@865 | 844 strip_locale_def |
al@865 | 845 ;; |
al@865 | 846 fs) |
al@865 | 847 # Compressors working in the $fs |
al@865 | 848 strip_package |
al@865 | 849 strip_mo_i18n |
al@865 | 850 ;; |
al@865 | 851 esac |
al@868 | 852 |
al@868 | 853 # Clean |
al@868 | 854 rm "$cache_stat" |
al@869 | 855 find $comp_cache_root -type f -links -2 -delete |