]> diplodocus.org Git - nmh/blob - SPECS/build-nmh-cygwin
Fixed patch section of prep().
[nmh] / SPECS / build-nmh-cygwin
1 #!/bin/bash
2 #
3 # ==========================================================================
4 # Based on Cygwin generic package build script, customized for nmh.
5 # Relies on nmh VERSION file.
6 #
7 # This script is incompatible with directory names that contain spaces, etc.
8 # To fix that, a whole bunch of shell variables need to be wrapped with "".
9 # ==========================================================================
10 #
11 # Generic package build script
12 #
13 # $Id: generic-build-script,v 1.47 2006/02/01 14:01:14 igor Exp $
14 #
15 # Package maintainers: if the original source is not distributed as a
16 # (possibly compressed) tarball, set the value of ${src_orig_pkg_name},
17 # and redefine the unpack() helper function appropriately.
18 # Also, if the Makefile rule to run the test suite is not "check", change
19 # the definition of ${test_rule} below.
20
21 # find out where the build script is located
22 tdir=`echo "$0" | sed 's%[\\/][^\\/][^\\/]*$%%'`
23 test "x$tdir" = "x$0" && tdir=.
24 scriptdir=`cd $tdir; pwd`
25 # find src directory.
26 # If scriptdir ends in SPECS, then topdir is $scriptdir/..
27 # If scriptdir ends in CYGWIN-PATCHES, then topdir is $scriptdir/../..
28 # Otherwise, we assume that topdir = scriptdir
29 topdir1=`echo ${scriptdir} | sed 's%/SPECS$%%'`
30 topdir2=`echo ${scriptdir} | sed 's%/CYGWIN-PATCHES$%%'`
31 if [ "x$topdir1" != "x$scriptdir" ] ; then # SPECS
32 topdir=`cd ${scriptdir}/..; pwd`
33 else
34 if [ "x$topdir2" != "x$scriptdir" ] ; then # CYGWIN-PATCHES
35 topdir=`cd ${scriptdir}/../..; pwd`
36 else
37 topdir=`cd ${scriptdir}; pwd`
38 fi
39 fi
40
41 # Change from generic-build-script: save pwd for later use.
42 pwd=`pwd`
43
44 # Change from generic-build-script: base version on contents of
45 # VERSION instead of script name. Looks first for VERSION in
46 # current directory, then in $topdir.
47 test -e VERSION && version=VERSION || version="${topdir}/VERSION"
48 if ! test -e "${version}"; then
49 echo "$0: need VERSION file"
50 exit 1
51 fi
52
53 tscriptname=nmh-`cat "${version}"`
54 # Change from generic-build-script: allow + in addition to - between
55 # VER and REL, e.g., 1.5+dev.
56 export PKG=`echo $tscriptname | sed -e 's/\-[^\-]*[+-][^+-]*$//'`
57 export VER=`echo $tscriptname | sed -e "s/${PKG}\-//" -e 's/[+-][^+-]*$//'`
58 export REL=`echo $tscriptname | sed -e "s/${PKG}\-${VER}\([+-]\)/\1/"`
59 # BASEPKG refers to the upstream base package
60 # SHORTPKG refers to the Cygwin package
61 # Normally, these are identical, but if the Cygwin package name is different
62 # from the upstream package name, you will want to redefine BASEPKG.
63 # Example: For Apache 2, BASEPKG=httpd-2.x.xx but SHORTPKG=apache2-2.x.xx
64 #
65 # Change from generic-build-script: added -${REL} to BASEPKG.
66 export BASEPKG=${PKG}-${VER}${REL}
67 export SHORTPKG=${PKG}-${VER}
68 export FULLPKG=${SHORTPKG}${REL}
69
70 # determine correct decompression option and tarball filename
71 export src_orig_pkg_name=
72 if [ -e "${src_orig_pkg_name}" ] ; then
73 export opt_decomp=? # Make sure tar punts if unpack() is not redefined
74 elif [ -e ${BASEPKG}.tar.bz2 ] ; then
75 export opt_decomp=j
76 export src_orig_pkg_name=${BASEPKG}.tar.bz2
77 elif [ -e ${BASEPKG}.tar.gz ] ; then
78 export opt_decomp=z
79 export src_orig_pkg_name=${BASEPKG}.tar.gz
80 elif [ -e ${BASEPKG}.tgz ] ; then
81 export opt_decomp=z
82 export src_orig_pkg_name=${BASEPKG}.tgz
83 elif [ -e ${BASEPKG}.tar ] ; then
84 export opt_decomp=
85 export src_orig_pkg_name=${BASEPKG}.tar
86 else
87 # Change from generic-build-script: build the tarball if it doesn't exist.
88 (cd "${topdir}" && make dist)
89 if [ -e "${topdir}/${BASEPKG}.tar.gz" ] ; then
90 export opt_decomp=z
91 export src_orig_pkg_name=${BASEPKG}.tar.gz
92 else
93 echo "Cannot find PKG:${PKG} VER:${VER} REL:${REL}. Rename $0 to"
94 echo "something more appropriate, and try again."
95 exit 1
96 fi
97 fi
98
99 export src_orig_pkg=${topdir}/${src_orig_pkg_name}
100
101 # determine correct names for generated files
102 export src_pkg_name=${FULLPKG}-src.tar.bz2
103 export src_patch_name=${FULLPKG}.patch
104 export bin_pkg_name=${FULLPKG}.tar.bz2
105 export log_pkg_name=${FULLPKG}-BUILDLOGS.tar.bz2
106
107 export configurelogname=${FULLPKG}-CONFIGURE.LOG
108 export makelogname=${FULLPKG}-MAKE.LOG
109 export checklogname=${FULLPKG}-CHECK.LOG
110 export installlogname=${FULLPKG}-INSTALL.LOG
111
112 # Change from generic-build-script: put src_pkg and bin_pkg in current
113 # directory.
114 export src_pkg=${pwd}/${src_pkg_name}
115 export src_patch="${topdir}"/${src_patch_name}
116 export bin_pkg=${pwd}/${bin_pkg_name}
117 export srcdir="${topdir}"/${BASEPKG}
118 export objdir=${srcdir}/.build
119 export instdir=${srcdir}/.inst
120 export srcinstdir=${srcdir}/.sinst
121 export buildlogdir=${srcdir}/.buildlogs
122 export configurelogfile=${srcinstdir}/${configurelogname}
123 export makelogfile=${srcinstdir}/${makelogname}
124 export checklogfile=${srcinstdir}/${checklogname}
125 export installlogfile=${srcinstdir}/${installlogname}
126
127 prefix=/usr
128 sysconfdir=/etc
129 localstatedir=/var
130 if [ -z "$MY_CFLAGS" ]; then
131 MY_CFLAGS="-O2"
132 fi
133 if [ -z "$MY_LDFLAGS" ]; then
134 MY_LDFLAGS=
135 fi
136
137 # Change from generic-build-script: removed ChangeLog because the nmh
138 # Makefile installs it.
139 export install_docs="\
140 ABOUT-NLS \
141 ANNOUNCE \
142 AUTHORS \
143 BUG-REPORTS \
144 CHANGES \
145 CONTRIBUTORS \
146 COPYING \
147 COPYRIGHT \
148 CREDITS \
149 CHANGELOG \
150 FAQ \
151 HOW-TO-CONTRIBUTE \
152 INSTALL \
153 KNOWNBUG \
154 LEGAL \
155 LICENSE \
156 NEWS \
157 NOTES \
158 PROGLIST \
159 README \
160 RELEASE_NOTES \
161 THANKS \
162 TODO \
163 USAGE \
164 "
165 export install_docs="`for i in ${install_docs}; do echo $i; done | sort -u`"
166 export test_rule=check
167 if [ -z "$SIG" ]; then
168 export SIG=0 # set to 1 to turn on signing by default
169 fi
170 # Sort in POSIX order.
171 export LC_ALL=C
172
173 # helper functions
174
175 # Provide help.
176 help() {
177 cat <<EOF
178 This is the cygwin packaging script for ${FULLPKG}.
179 Usage: $0 [<option>...] <action>...
180 Options are:
181 help, --help Print this message
182 version, --version Print the version message
183 with_logs, --logs Create logs of remaining steps
184 Actions are:
185 prep Unpack and patch into ${srcdir}
186 mkdirs Make hidden directories needed during build
187 conf, configure Configure the package (./configure)
188 reconf Rerun configure
189 build, make Build the package (make)
190 check, test Run the testsuite (make ${test_rule})
191 clean Remove built files (make clean)
192 install Install package to staging area (make install)
193 list List package contents
194 depend List package dependencies
195 strip Strip package executables
196 pkg, package Prepare the binary package ${bin_pkg_name}
197 mkpatch Prepare the patch file ${src_patch_name}
198 acceptpatch Copy patch file ${src_patch_name} to ${topdir}
199 spkg, src-package Prepare the source package ${src_pkg_name}
200 finish Remove source directory ${srcdir}
201 checksig Validate GPG signatures (requires gpg)
202 first Full run for spkg (mkdirs, spkg, finish)
203 almostall Full run for bin pkg, except for finish
204 all Full run for bin pkg
205 EOF
206 }
207
208 # Provide version of generic-build-script modified to make this script.
209 version() {
210 vers=`echo '$Revision: 1.47 $' | sed -e 's/Revision: //' -e 's/ *\\$//g'`
211 echo "$0 based on generic-build-script $vers"
212 }
213
214 # Unpacks the original package source archive into ./${BASEPKG}/.
215 # Change this if the original package was not tarred
216 # or if it doesn't unpack to a correct directory.
217 unpack() {
218 tar xv${opt_decomp}f "$1"
219 }
220
221 # Make the hidden directories used by this script.
222 mkdirs() {
223 (cd ${topdir} && \
224 rm -fr ${objdir} ${instdir} ${srcinstdir} && \
225 mkdir -p ${objdir} && \
226 mkdir -p ${instdir} && \
227 mkdir -p ${srcinstdir} )
228 }
229 mkdirs_log() {
230 (cd ${topdir} && \
231 mkdirs "$@" && \
232 rm -fr ${buildlogdir} && \
233 mkdir -p ${buildlogdir} )
234 }
235
236 # Unpack the original tarball, and get everything set up for this script.
237 # Change from generic-build-script: cd to ${srcdir} instead of ${topdir}.
238 # Change from generic-build-script: use -p1 instead of -p0 patch option.
239 prep() {
240 (cd ${topdir} && \
241 unpack ${src_orig_pkg} && \
242 cd ${srcdir} && \
243 if [ -f ${src_patch} ] ; then \
244 patch -Z -p1 < ${src_patch} ;\
245 fi && \
246 mkdirs )
247 }
248 prep_log() {
249 prep "$@" && \
250 mkdirs_log && \
251 if [ -f ${topdir}/${log_pkg_name} ] ; then \
252 # Change from generic-build-script: do the following in subshell
253 # so that cd isn't permanent.
254 (cd ${buildlogdir} && \
255 tar xvjf "${topdir}"/${log_pkg_name})
256 fi
257 }
258
259 # Configure the package.
260 conf() {
261 (cd ${objdir} && \
262 CFLAGS="${MY_CFLAGS}" LDFLAGS="${MY_LDFLAGS}" \
263 ${srcdir}/configure \
264 --srcdir=${srcdir} --prefix="${prefix}" \
265 --exec-prefix='${prefix}' --sysconfdir="${sysconfdir}" \
266 --libdir='${prefix}/lib' --includedir='${prefix}/include' \
267 --mandir='${prefix}/share/man' --infodir='${prefix}/share/info' \
268 --libexecdir='${sbindir}' --localstatedir="${localstatedir}" \
269 --datadir='${prefix}/share' )
270 }
271 conf_log() {
272 conf "$@" 2>&1 | tee ${configurelogfile}
273 return ${PIPESTATUS[0]}
274 }
275
276 # Rerun configure to pick up changes in the environment.
277 reconf() {
278 (cd ${topdir} && \
279 rm -fr ${objdir} && \
280 mkdir -p ${objdir} && \
281 conf )
282 }
283 reconf_log() {
284 reconf "$@" 2>&1 | tee ${configurelogfile}
285 return ${PIPESTATUS[0]}
286 }
287
288 # Run make.
289 build() {
290 (cd ${objdir} && \
291 make CFLAGS="${MY_CFLAGS}" )
292 }
293 build_log() {
294 build "$@" 2>&1 | tee ${makelogfile}
295 return ${PIPESTATUS[0]}
296 }
297
298 # Run the package testsuite.
299 check() {
300 (cd ${objdir} && \
301 make -k ${test_rule} )
302 }
303 check_log() {
304 check "$@" 2>&1 | tee ${checklogfile}
305 return ${PIPESTATUS[0]}
306 }
307
308 # Remove files created by configure and make.
309 clean() {
310 (cd ${objdir} && \
311 make clean )
312 }
313
314 # Install the package, with DESTDIR set to '.inst'.
315 # Change from generic-build-script: added ":;" after "find ... | gzip"
316 # because it returns non-zero status.
317 install() {
318 (cd ${objdir} && \
319 rm -fr ${instdir}/* && \
320 make install DESTDIR=${instdir} && \
321 for f in ${prefix}/share/info/dir ${prefix}/info/dir ; do \
322 if [ -f ${instdir}${f} ] ; then \
323 rm -f ${instdir}${f} ; \
324 fi ;\
325 done &&\
326 for d in ${prefix}/share/doc/${SHORTPKG} ${prefix}/share/doc/Cygwin ; do \
327 if [ ! -d ${instdir}${d} ] ; then \
328 mkdir -p ${instdir}${d} ;\
329 fi ;\
330 done &&\
331 if [ -d ${instdir}${prefix}/share/info ] ; then \
332 find ${instdir}${prefix}/share/info -name "*.info" | xargs -r gzip -q ; \
333 fi && \
334 if [ -d ${instdir}${prefix}/share/man ] ; then \
335 find ${instdir}${prefix}/share/man -name "*.1" -o -name "*.3" -o \
336 -name "*.3x" -o -name "*.3pm" -o -name "*.5" -o -name "*.6" -o \
337 -name "*.7" -o -name "*.8" | xargs -r gzip -q ; :; \
338 fi && \
339 templist="" && \
340 for fp in ${install_docs} ; do \
341 case "$fp" in \
342 */) templist="$templist `find ${srcdir}/$fp -type f`" ;;
343 *) for f in ${srcdir}/$fp ; do \
344 if [ -f $f ] ; then \
345 templist="$templist $f"; \
346 fi ; \
347 done ;; \
348 esac ; \
349 done && \
350 if [ ! "x$templist" = "x" ]; then \
351 /usr/bin/install -m 644 $templist \
352 ${instdir}${prefix}/share/doc/${SHORTPKG} ; \
353 fi && \
354 if [ -f ${srcdir}/CYGWIN-PATCHES/${PKG}.README ]; then \
355 /usr/bin/install -m 644 ${srcdir}/CYGWIN-PATCHES/${PKG}.README \
356 ${instdir}${prefix}/share/doc/Cygwin/${SHORTPKG}.README ; \
357 elif [ -f ${srcdir}/CYGWIN-PATCHES/README ] ; then \
358 /usr/bin/install -m 644 ${srcdir}/CYGWIN-PATCHES/README \
359 ${instdir}${prefix}/share/doc/Cygwin/${SHORTPKG}.README ; \
360 fi && \
361 if [ -f ${srcdir}/CYGWIN-PATCHES/${PKG}.sh ] ; then \
362 if [ ! -d ${instdir}${sysconfdir}/postinstall ]; then \
363 mkdir -p ${instdir}${sysconfdir}/postinstall ; \
364 fi && \
365 /usr/bin/install -m 755 ${srcdir}/CYGWIN-PATCHES/${PKG}.sh \
366 ${instdir}${sysconfdir}/postinstall/${PKG}.sh ; \
367 elif [ -f ${srcdir}/CYGWIN-PATCHES/postinstall.sh ] ; then \
368 if [ ! -d ${instdir}${sysconfdir}/postinstall ]; then \
369 mkdir -p ${instdir}${sysconfdir}/postinstall ; \
370 fi && \
371 /usr/bin/install -m 755 ${srcdir}/CYGWIN-PATCHES/postinstall.sh \
372 ${instdir}${sysconfdir}/postinstall/${PKG}.sh ; \
373 fi && \
374 if [ -f ${srcdir}/CYGWIN-PATCHES/preremove.sh ] ; then \
375 if [ ! -d ${instdir}${sysconfdir}/preremove ]; then \
376 mkdir -p ${instdir}${sysconfdir}/preremove ; \
377 fi && \
378 /usr/bin/install -m 755 ${srcdir}/CYGWIN-PATCHES/preremove.sh \
379 ${instdir}${sysconfdir}/preremove/${PKG}.sh ; \
380 fi &&
381 if [ -f ${srcdir}/CYGWIN-PATCHES/manifest.lst ] ; then
382 if [ ! -d ${instdir}${sysconfdir}/preremove ]; then
383 mkdir -p ${instdir}${sysconfdir}/preremove ;
384 fi &&
385 /usr/bin/install -m 644 ${srcdir}/CYGWIN-PATCHES/manifest.lst \
386 ${instdir}${sysconfdir}/preremove/${PKG}-manifest.lst ;
387 fi )
388 }
389 install_log() {
390 install "$@" 2>&1 | tee ${installlogfile}
391 return ${PIPESTATUS[0]}
392 }
393
394 # Strip all binaries.
395 strip() {
396 (cd ${instdir} && \
397 find . -name "*.dll" -or -name "*.exe" | xargs -r strip 2>&1 ; \
398 true )
399 }
400
401 # List all non-hidden files that belong to the package.
402 list() {
403 (cd ${instdir} && \
404 find . -name "*" ! -type d | sed 's%^\.% %' | sort ; \
405 true )
406 }
407
408 # List the static .dll dependencies of the package. This does not pick up
409 # dynamic dependencies (whether or not libtool was used), nor does it pick
410 # up program dependencies, such as system() depending on /bin/sh.
411 depend() {
412 (cd ${instdir} && \
413 find ${instdir} -name "*.exe" -o -name "*.dll" | xargs -r cygcheck | \
414 sed -ne '/^ [^ ]/ s,\\,/,gp' | sort -bu | \
415 xargs -r -n1 cygpath -u | xargs -r cygcheck -f | sed 's%^% %' | sort -u ; \
416 true )
417 }
418
419 # Build the binary package tarball.
420 pkg() {
421 (cd ${instdir} && \
422 tar cvjf ${bin_pkg} * )
423 }
424
425 # Compare the original tarball against cygwin modifications.
426 mkpatch() {
427 (cd ${srcdir} && \
428 find . -name "autom4te.cache" | xargs -r rm -rf ; \
429 unpack ${src_orig_pkg} && \
430 mv ${BASEPKG} ../${BASEPKG}-orig && \
431 cd ${topdir} && \
432 diff -urN -x '.build' -x '.inst' -x '.sinst' -x '.buildlogs' \
433 ${BASEPKG}-orig ${BASEPKG} > \
434 ${srcinstdir}/${src_patch_name} ; \
435 rm -rf ${BASEPKG}-orig )
436 }
437
438 # Note: maintainer-only functionality
439 acceptpatch() {
440 cp --backup=numbered ${srcinstdir}/${src_patch_name} ${topdir}
441 }
442
443 # Build the source tarball.
444 # Change from generic-build-script: added VERSION file.
445 spkg() {
446 (mkpatch && \
447 if [ "${SIG}" -eq 1 ] ; then \
448 name=${srcinstdir}/${src_patch_name} text="PATCH" sigfile ; \
449 fi && \
450 cp ${src_orig_pkg} ${srcinstdir}/${src_orig_pkg_name} && \
451 if [ -e ${src_orig_pkg}.sig ] ; then \
452 cp ${src_orig_pkg}.sig ${srcinstdir}/ ; \
453 fi && \
454 cp $0 ${srcinstdir}/`basename $0` && \
455 name=$0 text="SCRIPT" sigfile && \
456 if [ "${SIG}" -eq 1 ] ; then \
457 cp $0.sig ${srcinstdir}/ ; \
458 fi && \
459 cp "${version}" "${srcinstdir}" && \
460 cd ${srcinstdir} && \
461 tar cvjf ${src_pkg} * )
462 }
463 spkg_log() {
464 spkg "$@" && \
465 (cd ${srcinstdir} && \
466 if [ -e ${configurelogname} -o -e ${makelogname} -o \
467 -e ${checklogname} -o -e ${installlogname} ]; then
468 tar --ignore-failed-read -cvjf ${log_pkg_name} \
469 ${configurelogname} ${makelogname} ${checklogname} ${installlogname} && \
470 rm -f \
471 ${configurelogname} ${makelogname} ${checklogname} ${installlogname} ; \
472 fi && \
473 tar uvjf ${src_pkg} * )
474 }
475
476 # Clean up everything.
477 finish() {
478 rm -rf ${srcdir}
479 }
480
481 # Generate GPG signatures.
482 sigfile() {
483 if [ \( "${SIG}" -eq 1 \) -a \( -e $name \) -a \( \( ! -e $name.sig \) -o \( $name -nt $name.sig \) \) ]; then \
484 if [ -x /usr/bin/gpg ]; then \
485 echo "$text signature need to be updated"; \
486 rm -f $name.sig; \
487 /usr/bin/gpg --detach-sign $name; \
488 else \
489 echo "You need the gnupg package installed in order to make signatures."; \
490 fi; \
491 fi
492 }
493
494 # Validate GPG signatures.
495 checksig() {
496 if [ -x /usr/bin/gpg ]; then \
497 if [ -e ${src_orig_pkg}.sig ]; then \
498 echo "ORIGINAL PACKAGE signature follows:"; \
499 /usr/bin/gpg --verify ${src_orig_pkg}.sig ${src_orig_pkg}; \
500 else \
501 echo "ORIGINAL PACKAGE signature missing."; \
502 fi; \
503 if [ -e $0.sig ]; then \
504 echo "SCRIPT signature follows:"; \
505 /usr/bin/gpg --verify $0.sig $0; \
506 else \
507 echo "SCRIPT signature missing."; \
508 fi; \
509 if [ -e ${src_patch}.sig ]; then \
510 echo "PATCH signature follows:"; \
511 /usr/bin/gpg --verify ${src_patch}.sig ${src_patch}; \
512 else \
513 echo "PATCH signature missing."; \
514 fi; \
515 else
516 echo "You need the gnupg package installed in order to check signatures." ; \
517 fi
518 }
519
520 f_mkdirs=mkdirs
521 f_prep=prep
522 f_conf=conf
523 f_reconf=conf
524 f_build=build
525 f_check=check
526 f_install=install
527 f_spkg=spkg
528
529 enablelogging() {
530 f_mkdirs=mkdirs_log && \
531 f_prep=prep_log && \
532 f_conf=conf_log && \
533 f_reconf=reconf_log && \
534 f_build=build_log && \
535 f_check=check_log && \
536 f_install=install_log && \
537 f_spkg=spkg_log
538 }
539
540 while test -n "$1" ; do
541 case $1 in
542 help|--help) help ; STATUS=$? ;;
543 version|--version) version ; STATUS=$? ;;
544 with_logs|--logs) enablelogging ; STATUS=$? ;;
545 prep) $f_prep ; STATUS=$? ;;
546 mkdirs) $f_mkdirs ; STATUS=$? ;;
547 conf) $f_conf ; STATUS=$? ;;
548 configure) $f_conf ; STATUS=$? ;;
549 reconf) $f_reconf ; STATUS=$? ;;
550 build) $f_build ; STATUS=$? ;;
551 make) $f_build ; STATUS=$? ;;
552 check) $f_check ; STATUS=$? ;;
553 test) $f_check ; STATUS=$? ;;
554 clean) $f_clean ; STATUS=$? ;;
555 install) $f_install ; STATUS=$? ;;
556 list) list ; STATUS=$? ;;
557 depend) depend ; STATUS=$? ;;
558 strip) strip ; STATUS=$? ;;
559 package) pkg ; STATUS=$? ;;
560 pkg) pkg ; STATUS=$? ;;
561 mkpatch) mkpatch ; STATUS=$? ;;
562 acceptpatch) acceptpatch ; STATUS=$? ;;
563 src-package) $f_spkg ; STATUS=$? ;;
564 spkg) $f_spkg ; STATUS=$? ;;
565 finish) finish ; STATUS=$? ;;
566 checksig) checksig ; STATUS=$? ;;
567 first) $f_mkdirs && $f_spkg && finish ; STATUS=$? ;;
568 almostall) checksig && $f_prep && $f_conf && $f_build && \
569 $f_install && strip && pkg && $f_spkg ; STATUS=$? ;;
570 all) checksig && $f_prep && $f_conf && $f_build && \
571 $f_install && strip && pkg && $f_spkg && finish ; \
572 STATUS=$? ;;
573 *) echo "Error: bad arguments" ; exit 1 ;;
574 esac
575 ( exit ${STATUS} ) || exit ${STATUS}
576 shift
577 done