Merge branch 'org.openembedded.dev' of git://git.openembedded.net/openembedded into...
[vuplus_openembedded] / classes / base.bbclass
1 BB_DEFAULT_TASK ?= "build"
2
3 # like os.path.join but doesn't treat absolute RHS specially
4 def base_path_join(a, *p):
5     path = a
6     for b in p:
7         if path == '' or path.endswith('/'):
8             path +=  b
9         else:
10             path += '/' + b
11     return path
12
13 # for MD5/SHA handling
14 def base_chk_load_parser(config_path):
15     import ConfigParser, os, bb
16     parser = ConfigParser.ConfigParser()
17     if not len(parser.read(config_path)) == 1:
18         bb.note("Can not open the '%s' ini file" % config_path)
19         raise Exception("Can not open the '%s'" % config_path)
20
21     return parser
22
23 def base_chk_file(parser, pn, pv, src_uri, localpath, data):
24     import os, bb
25     no_checksum = False
26     # Try PN-PV-SRC_URI first and then try PN-SRC_URI
27     # we rely on the get method to create errors
28     pn_pv_src = "%s-%s-%s" % (pn,pv,src_uri)
29     pn_src    = "%s-%s" % (pn,src_uri)
30     if parser.has_section(pn_pv_src):
31         md5    = parser.get(pn_pv_src, "md5")
32         sha256 = parser.get(pn_pv_src, "sha256")
33     elif parser.has_section(pn_src):
34         md5    = parser.get(pn_src, "md5")
35         sha256 = parser.get(pn_src, "sha256")
36     elif parser.has_section(src_uri):
37         md5    = parser.get(src_uri, "md5")
38         sha256 = parser.get(src_uri, "sha256")
39     else:
40         no_checksum = True
41
42     # md5 and sha256 should be valid now
43     if not os.path.exists(localpath):
44         bb.note("The localpath does not exist '%s'" % localpath)
45         raise Exception("The path does not exist '%s'" % localpath)
46
47
48     # call md5(sum) and shasum
49     try:
50         md5pipe = os.popen('md5sum ' + localpath)
51         md5data = (md5pipe.readline().split() or [ "" ])[0]
52         md5pipe.close()
53     except OSError:
54         raise Exception("Executing md5sum failed")
55
56     try:
57         shapipe = os.popen('PATH=%s oe_sha256sum %s' % (bb.data.getVar('PATH', data, True), localpath))
58         shadata = (shapipe.readline().split() or [ "" ])[0]
59         shapipe.close()
60     except OSError:
61         raise Exception("Executing shasum failed")
62
63     if no_checksum == True:     # we do not have conf/checksums.ini entry
64         try:
65             file = open("%s/checksums.ini" % bb.data.getVar("TMPDIR", data, 1), "a")
66         except:
67             return False
68
69         if not file:
70             raise Exception("Creating checksums.ini failed")
71         
72         file.write("[%s]\nmd5=%s\nsha256=%s\n\n" % (src_uri, md5data, shadata))
73         file.close()
74         return False
75
76     if not md5 == md5data:
77         bb.note("The MD5Sums did not match. Wanted: '%s' and Got: '%s'" % (md5,md5data))
78         raise Exception("MD5 Sums do not match. Wanted: '%s' Got: '%s'" % (md5, md5data))
79
80     if not sha256 == shadata:
81         bb.note("The SHA256 Sums do not match. Wanted: '%s' Got: '%s'" % (sha256,shadata))
82         raise Exception("SHA256 Sums do not match. Wanted: '%s' Got: '%s'" % (sha256, shadata))
83
84     return True
85
86
87 def base_dep_prepend(d):
88         import bb
89         #
90         # Ideally this will check a flag so we will operate properly in
91         # the case where host == build == target, for now we don't work in
92         # that case though.
93         #
94         deps = "shasum-native "
95         if bb.data.getVar('PN', d, True) == "shasum-native":
96                 deps = ""
97
98         # INHIBIT_DEFAULT_DEPS doesn't apply to the patch command.  Whether or  not
99         # we need that built is the responsibility of the patch function / class, not
100         # the application.
101         if not bb.data.getVar('INHIBIT_DEFAULT_DEPS', d):
102                 if (bb.data.getVar('HOST_SYS', d, 1) !=
103                     bb.data.getVar('BUILD_SYS', d, 1)):
104                         deps += " virtual/${TARGET_PREFIX}gcc virtual/libc "
105         return deps
106
107 def base_read_file(filename):
108         import bb
109         try:
110                 f = file( filename, "r" )
111         except IOError, reason:
112                 return "" # WARNING: can't raise an error now because of the new RDEPENDS handling. This is a bit ugly. :M:
113         else:
114                 return f.read().strip()
115         return None
116
117 def base_conditional(variable, checkvalue, truevalue, falsevalue, d):
118         import bb
119         if bb.data.getVar(variable,d,1) == checkvalue:
120                 return truevalue
121         else:
122                 return falsevalue
123
124 def base_less_or_equal(variable, checkvalue, truevalue, falsevalue, d):
125         import bb
126         if float(bb.data.getVar(variable,d,1)) <= float(checkvalue):
127                 return truevalue
128         else:
129                 return falsevalue
130
131 def base_version_less_or_equal(variable, checkvalue, truevalue, falsevalue, d):
132     import bb
133     result = bb.vercmp(bb.data.getVar(variable,d,True), checkvalue)
134     if result <= 0:
135         return truevalue
136     else:
137         return falsevalue
138
139 def base_contains(variable, checkvalues, truevalue, falsevalue, d):
140         import bb
141         matches = 0
142         if type(checkvalues).__name__ == "str":
143                 checkvalues = [checkvalues]
144         for value in checkvalues:
145                 if bb.data.getVar(variable,d,1).find(value) != -1:      
146                         matches = matches + 1
147         if matches == len(checkvalues):
148                 return truevalue                
149         return falsevalue
150
151 def base_both_contain(variable1, variable2, checkvalue, d):
152        import bb
153        if bb.data.getVar(variable1,d,1).find(checkvalue) != -1 and bb.data.getVar(variable2,d,1).find(checkvalue) != -1:
154                return checkvalue
155        else:
156                return ""
157
158 DEPENDS_prepend="${@base_dep_prepend(d)} "
159
160 # Returns PN with various suffixes removed
161 # or PN if no matching suffix was found.
162 def base_package_name(d):
163   import bb;
164
165   pn = bb.data.getVar('PN', d, 1)
166   if pn.endswith("-native"):
167                 pn = pn[0:-7]
168   elif pn.endswith("-cross"):
169                 pn = pn[0:-6]
170   elif pn.endswith("-initial"):
171                 pn = pn[0:-8]
172   elif pn.endswith("-intermediate"):
173                 pn = pn[0:-13]
174
175   return pn
176
177 def base_set_filespath(path, d):
178         import os, bb
179         filespath = []
180         # The ":" ensures we have an 'empty' override
181         overrides = (bb.data.getVar("OVERRIDES", d, 1) or "") + ":"
182         for p in path:
183                 for o in overrides.split(":"):
184                         filespath.append(os.path.join(p, o))
185         return ":".join(filespath)
186
187 FILESPATH = "${@base_set_filespath([ "${FILE_DIRNAME}/${PF}", "${FILE_DIRNAME}/${P}", "${FILE_DIRNAME}/${PN}", "${FILE_DIRNAME}/${BP}", "${FILE_DIRNAME}/${BPN}", "${FILE_DIRNAME}/files", "${FILE_DIRNAME}" ], d)}"
188
189 def oe_filter(f, str, d):
190         from re import match
191         return " ".join(filter(lambda x: match(f, x, 0), str.split()))
192
193 def oe_filter_out(f, str, d):
194         from re import match
195         return " ".join(filter(lambda x: not match(f, x, 0), str.split()))
196
197 die() {
198         oefatal "$*"
199 }
200
201 oenote() {
202         echo "NOTE:" "$*"
203 }
204
205 oewarn() {
206         echo "WARNING:" "$*"
207 }
208
209 oefatal() {
210         echo "FATAL:" "$*"
211         exit 1
212 }
213
214 oedebug() {
215         test $# -ge 2 || {
216                 echo "Usage: oedebug level \"message\""
217                 exit 1
218         }
219
220         test ${OEDEBUG:-0} -ge $1 && {
221                 shift
222                 echo "DEBUG:" $*
223         }
224 }
225
226 oe_runmake() {
227         if [ x"$MAKE" = x ]; then MAKE=make; fi
228         oenote ${MAKE} ${EXTRA_OEMAKE} "$@"
229         ${MAKE} ${EXTRA_OEMAKE} "$@" || die "oe_runmake failed"
230 }
231
232 oe_soinstall() {
233         # Purpose: Install shared library file and
234         #          create the necessary links
235         # Example:
236         #
237         # oe_
238         #
239         #oenote installing shared library $1 to $2
240         #
241         libname=`basename $1`
242         install -m 755 $1 $2/$libname
243         sonamelink=`${HOST_PREFIX}readelf -d $1 |grep 'Library soname:' |sed -e 's/.*\[\(.*\)\].*/\1/'`
244         solink=`echo $libname | sed -e 's/\.so\..*/.so/'`
245         ln -sf $libname $2/$sonamelink
246         ln -sf $libname $2/$solink
247 }
248
249 oe_libinstall() {
250         # Purpose: Install a library, in all its forms
251         # Example
252         #
253         # oe_libinstall libltdl ${STAGING_LIBDIR}/
254         # oe_libinstall -C src/libblah libblah ${D}/${libdir}/
255         dir=""
256         libtool=""
257         silent=""
258         require_static=""
259         require_shared=""
260         staging_install=""
261         while [ "$#" -gt 0 ]; do
262                 case "$1" in
263                 -C)
264                         shift
265                         dir="$1"
266                         ;;
267                 -s)
268                         silent=1
269                         ;;
270                 -a)
271                         require_static=1
272                         ;;
273                 -so)
274                         require_shared=1
275                         ;;
276                 -*)
277                         oefatal "oe_libinstall: unknown option: $1"
278                         ;;
279                 *)
280                         break;
281                         ;;
282                 esac
283                 shift
284         done
285
286         libname="$1"
287         shift
288         destpath="$1"
289         if [ -z "$destpath" ]; then
290                 oefatal "oe_libinstall: no destination path specified"
291         fi
292         if echo "$destpath/" | egrep '^${STAGING_LIBDIR}/' >/dev/null
293         then
294                 staging_install=1
295         fi
296
297         __runcmd () {
298                 if [ -z "$silent" ]; then
299                         echo >&2 "oe_libinstall: $*"
300                 fi
301                 $*
302         }
303
304         if [ -z "$dir" ]; then
305                 dir=`pwd`
306         fi
307
308         dotlai=$libname.lai
309
310         # Sanity check that the libname.lai is unique
311         number_of_files=`(cd $dir; find . -name "$dotlai") | wc -l`
312         if [ $number_of_files -gt 1 ]; then
313                 oefatal "oe_libinstall: $dotlai is not unique in $dir"
314         fi
315
316
317         dir=$dir`(cd $dir;find . -name "$dotlai") | sed "s/^\.//;s/\/$dotlai\$//;q"`
318         olddir=`pwd`
319         __runcmd cd $dir
320
321         lafile=$libname.la
322
323         # If such file doesn't exist, try to cut version suffix
324         if [ ! -f "$lafile" ]; then
325                 libname1=`echo "$libname" | sed 's/-[0-9.]*$//'`
326                 lafile1=$libname.la
327                 if [ -f "$lafile1" ]; then
328                         libname=$libname1
329                         lafile=$lafile1
330                 fi
331         fi
332
333         if [ -f "$lafile" ]; then
334                 # libtool archive
335                 eval `cat $lafile|grep "^library_names="`
336                 libtool=1
337         else
338                 library_names="$libname.so* $libname.dll.a"
339         fi
340
341         __runcmd install -d $destpath/
342         dota=$libname.a
343         if [ -f "$dota" -o -n "$require_static" ]; then
344                 __runcmd install -m 0644 $dota $destpath/
345         fi
346         if [ -f "$dotlai" -a -n "$libtool" ]; then
347                 if test -n "$staging_install"
348                 then
349                         # stop libtool using the final directory name for libraries
350                         # in staging:
351                         __runcmd rm -f $destpath/$libname.la
352                         __runcmd sed -e 's/^installed=yes$/installed=no/' \
353                                      -e '/^dependency_libs=/s,${WORKDIR}[[:alnum:]/\._+-]*/\([[:alnum:]\._+-]*\),${STAGING_LIBDIR}/\1,g' \
354                                      -e "/^dependency_libs=/s,\([[:space:]']\)${libdir},\1${STAGING_LIBDIR},g" \
355                                      $dotlai >$destpath/$libname.la
356                 else
357                         __runcmd install -m 0644 $dotlai $destpath/$libname.la
358                 fi
359         fi
360
361         for name in $library_names; do
362                 files=`eval echo $name`
363                 for f in $files; do
364                         if [ ! -e "$f" ]; then
365                                 if [ -n "$libtool" ]; then
366                                         oefatal "oe_libinstall: $dir/$f not found."
367                                 fi
368                         elif [ -L "$f" ]; then
369                                 __runcmd cp -P "$f" $destpath/
370                         elif [ ! -L "$f" ]; then
371                                 libfile="$f"
372                                 __runcmd install -m 0755 $libfile $destpath/
373                         fi
374                 done
375         done
376
377         if [ -z "$libfile" ]; then
378                 if  [ -n "$require_shared" ]; then
379                         oefatal "oe_libinstall: unable to locate shared library"
380                 fi
381         elif [ -z "$libtool" ]; then
382                 # special case hack for non-libtool .so.#.#.# links
383                 baselibfile=`basename "$libfile"`
384                 if (echo $baselibfile | grep -qE '^lib.*\.so\.[0-9.]*$'); then
385                         sonamelink=`${HOST_PREFIX}readelf -d $libfile |grep 'Library soname:' |sed -e 's/.*\[\(.*\)\].*/\1/'`
386                         solink=`echo $baselibfile | sed -e 's/\.so\..*/.so/'`
387                         if [ -n "$sonamelink" -a x"$baselibfile" != x"$sonamelink" ]; then
388                                 __runcmd ln -sf $baselibfile $destpath/$sonamelink
389                         fi
390                         __runcmd ln -sf $baselibfile $destpath/$solink
391                 fi
392         fi
393
394         __runcmd cd "$olddir"
395 }
396
397 def package_stagefile(file, d):
398     import bb, os
399
400     if bb.data.getVar('PSTAGING_ACTIVE', d, True) == "1":
401         destfile = file.replace(bb.data.getVar("TMPDIR", d, 1), bb.data.getVar("PSTAGE_TMPDIR_STAGE", d, 1))
402         bb.mkdirhier(os.path.dirname(destfile))
403         #print "%s to %s" % (file, destfile)
404         bb.copyfile(file, destfile)
405
406 package_stagefile_shell() {
407         if [ "$PSTAGING_ACTIVE" = "1" ]; then
408                 srcfile=$1
409                 destfile=`echo $srcfile | sed s#${TMPDIR}#${PSTAGE_TMPDIR_STAGE}#`
410                 destdir=`dirname $destfile`
411                 mkdir -p $destdir
412                 cp -dp $srcfile $destfile
413         fi
414 }
415
416 oe_machinstall() {
417         # Purpose: Install machine dependent files, if available
418         #          If not available, check if there is a default
419         #          If no default, just touch the destination
420         # Example:
421         #                $1  $2   $3         $4
422         # oe_machinstall -m 0644 fstab ${D}/etc/fstab
423         #
424         # TODO: Check argument number?
425         #
426         filename=`basename $3`
427         dirname=`dirname $3`
428
429         for o in `echo ${OVERRIDES} | tr ':' ' '`; do
430                 if [ -e $dirname/$o/$filename ]; then
431                         oenote $dirname/$o/$filename present, installing to $4
432                         install $1 $2 $dirname/$o/$filename $4
433                         return
434                 fi
435         done
436 #       oenote overrides specific file NOT present, trying default=$3...
437         if [ -e $3 ]; then
438                 oenote $3 present, installing to $4
439                 install $1 $2 $3 $4
440         else
441                 oenote $3 NOT present, touching empty $4
442                 touch $4
443         fi
444 }
445
446 addtask listtasks
447 do_listtasks[nostamp] = "1"
448 python do_listtasks() {
449         import sys
450         # emit variables and shell functions
451         #bb.data.emit_env(sys.__stdout__, d)
452         # emit the metadata which isnt valid shell
453         for e in d.keys():
454                 if bb.data.getVarFlag(e, 'task', d):
455                         sys.__stdout__.write("%s\n" % e)
456 }
457
458 addtask clean
459 do_clean[dirs] = "${TOPDIR}"
460 do_clean[nostamp] = "1"
461 python base_do_clean() {
462         """clear the build and temp directories"""
463         dir = bb.data.expand("${WORKDIR}", d)
464         if dir == '//': raise bb.build.FuncFailed("wrong DATADIR")
465         bb.note("removing " + dir)
466         os.system('rm -rf ' + dir)
467
468         dir = "%s.*" % bb.data.expand(bb.data.getVar('STAMP', d), d)
469         bb.note("removing " + dir)
470         os.system('rm -f '+ dir)
471 }
472
473 #Uncomment this for bitbake 1.8.12
474 #addtask rebuild after do_${BB_DEFAULT_TASK}
475 addtask rebuild
476 do_rebuild[dirs] = "${TOPDIR}"
477 do_rebuild[nostamp] = "1"
478 python base_do_rebuild() {
479         """rebuild a package"""
480         from bb import __version__
481         try:
482                 from distutils.version import LooseVersion
483         except ImportError:
484                 def LooseVersion(v): print "WARNING: sanity.bbclass can't compare versions without python-distutils"; return 1
485         if (LooseVersion(__version__) < LooseVersion('1.8.11')):
486                 bb.build.exec_func('do_clean', d)
487                 bb.build.exec_task('do_' + bb.data.getVar('BB_DEFAULT_TASK', d, 1), d)
488 }
489
490 addtask mrproper
491 do_mrproper[dirs] = "${TOPDIR}"
492 do_mrproper[nostamp] = "1"
493 python base_do_mrproper() {
494         """clear downloaded sources, build and temp directories"""
495         dir = bb.data.expand("${DL_DIR}", d)
496         if dir == '/': bb.build.FuncFailed("wrong DATADIR")
497         bb.debug(2, "removing " + dir)
498         os.system('rm -rf ' + dir)
499         bb.build.exec_func('do_clean', d)
500 }
501
502 SCENEFUNCS += "base_scenefunction"
503                                                                                         
504 python base_do_setscene () {
505         for f in (bb.data.getVar('SCENEFUNCS', d, 1) or '').split():
506                 bb.build.exec_func(f, d)
507         if not os.path.exists(bb.data.getVar('STAMP', d, 1) + ".do_setscene"):
508                 bb.build.make_stamp("do_setscene", d)
509 }
510 do_setscene[selfstamp] = "1"
511 addtask setscene before do_fetch
512
513 python base_scenefunction () {
514         stamp = bb.data.getVar('STAMP', d, 1) + ".needclean"
515         if os.path.exists(stamp):
516                 bb.build.exec_func("do_clean", d)
517 }
518
519
520 addtask fetch
521 do_fetch[dirs] = "${DL_DIR}"
522 do_fetch[depends] = "shasum-native:do_populate_staging"
523 python base_do_fetch() {
524         import sys
525
526         localdata = bb.data.createCopy(d)
527         bb.data.update_data(localdata)
528
529         src_uri = bb.data.getVar('SRC_URI', localdata, 1)
530         if not src_uri:
531                 return 1
532
533         try:
534                 bb.fetch.init(src_uri.split(),d)
535         except bb.fetch.NoMethodError:
536                 (type, value, traceback) = sys.exc_info()
537                 raise bb.build.FuncFailed("No method: %s" % value)
538
539         try:
540                 bb.fetch.go(localdata)
541         except bb.fetch.MissingParameterError:
542                 (type, value, traceback) = sys.exc_info()
543                 raise bb.build.FuncFailed("Missing parameters: %s" % value)
544         except bb.fetch.FetchError:
545                 (type, value, traceback) = sys.exc_info()
546                 raise bb.build.FuncFailed("Fetch failed: %s" % value)
547         except bb.fetch.MD5SumError:
548                 (type, value, traceback) = sys.exc_info()
549                 raise bb.build.FuncFailed("MD5  failed: %s" % value)
550         except:
551                 (type, value, traceback) = sys.exc_info()
552                 raise bb.build.FuncFailed("Unknown fetch Error: %s" % value)
553
554
555         # Verify the SHA and MD5 sums we have in OE and check what do
556         # in
557         check_sum = bb.which(bb.data.getVar('BBPATH', d, True), "conf/checksums.ini")
558         if not check_sum:
559                 bb.note("No conf/checksums.ini found, not checking checksums")
560                 return
561
562         try:
563                 parser = base_chk_load_parser(check_sum)
564         except:
565                 bb.note("Creating the CheckSum parser failed")
566                 return
567
568         pv = bb.data.getVar('PV', d, True)
569         pn = bb.data.getVar('PN', d, True)
570
571         # Check each URI
572         for url in src_uri.split():
573                 localpath = bb.data.expand(bb.fetch.localpath(url, localdata), localdata)
574                 (type,host,path,_,_,_) = bb.decodeurl(url)
575                 uri = "%s://%s%s" % (type,host,path)
576                 try:
577                         if type == "http" or type == "https" or type == "ftp" or type == "ftps":
578                                 if not base_chk_file(parser, pn, pv,uri, localpath, d):
579                                         if not bb.data.getVar("OE_ALLOW_INSECURE_DOWNLOADS",d, True):
580                                                 bb.fatal("%s-%s: %s has no entry in conf/checksums.ini, not checking URI" % (pn,pv,uri))
581                                         else:
582                                                 bb.note("%s-%s: %s has no entry in conf/checksums.ini, not checking URI" % (pn,pv,uri))
583                 except Exception:
584                         raise bb.build.FuncFailed("Checksum of '%s' failed" % uri)
585 }
586
587 addtask fetchall after do_fetch
588 do_fetchall[recrdeptask] = "do_fetch"
589 base_do_fetchall() {
590         :
591 }
592
593 addtask checkuri
594 do_checkuri[nostamp] = "1"
595 python do_checkuri() {
596         import sys
597
598         localdata = bb.data.createCopy(d)
599         bb.data.update_data(localdata)
600
601         src_uri = bb.data.getVar('SRC_URI', localdata, 1)
602
603         try:
604                 bb.fetch.init(src_uri.split(),d)
605         except bb.fetch.NoMethodError:
606                 (type, value, traceback) = sys.exc_info()
607                 raise bb.build.FuncFailed("No method: %s" % value)
608
609         try:
610                 bb.fetch.checkstatus(localdata)
611         except bb.fetch.MissingParameterError:
612                 (type, value, traceback) = sys.exc_info()
613                 raise bb.build.FuncFailed("Missing parameters: %s" % value)
614         except bb.fetch.FetchError:
615                 (type, value, traceback) = sys.exc_info()
616                 raise bb.build.FuncFailed("Fetch failed: %s" % value)
617         except bb.fetch.MD5SumError:
618                 (type, value, traceback) = sys.exc_info()
619                 raise bb.build.FuncFailed("MD5  failed: %s" % value)
620         except:
621                 (type, value, traceback) = sys.exc_info()
622                 raise bb.build.FuncFailed("Unknown fetch Error: %s" % value)
623 }
624
625 addtask checkuriall after do_checkuri
626 do_checkuriall[recrdeptask] = "do_checkuri"
627 do_checkuriall[nostamp] = "1"
628 base_do_checkuriall() {
629         :
630 }
631
632 addtask buildall after do_build
633 do_buildall[recrdeptask] = "do_build"
634 base_do_buildall() {
635         :
636 }
637
638
639 def oe_unpack_file(file, data, url = None):
640         import bb, os
641         if not url:
642                 url = "file://%s" % file
643         dots = file.split(".")
644         if dots[-1] in ['gz', 'bz2', 'Z']:
645                 efile = os.path.join(bb.data.getVar('WORKDIR', data, 1),os.path.basename('.'.join(dots[0:-1])))
646         else:
647                 efile = file
648         cmd = None
649         if file.endswith('.tar'):
650                 cmd = 'tar x --no-same-owner -f %s' % file
651         elif file.endswith('.tgz') or file.endswith('.tar.gz') or file.endswith('.tar.Z'):
652                 cmd = 'tar xz --no-same-owner -f %s' % file
653         elif file.endswith('.tbz') or file.endswith('.tbz2') or file.endswith('.tar.bz2'):
654                 cmd = 'bzip2 -dc %s | tar x --no-same-owner -f -' % file
655         elif file.endswith('.gz') or file.endswith('.Z') or file.endswith('.z'):
656                 cmd = 'gzip -dc %s > %s' % (file, efile)
657         elif file.endswith('.bz2'):
658                 cmd = 'bzip2 -dc %s > %s' % (file, efile)
659         elif file.endswith('.zip') or file.endswith('.jar'):
660                 cmd = 'unzip -q -o'
661                 (type, host, path, user, pswd, parm) = bb.decodeurl(url)
662                 if 'dos' in parm:
663                         cmd = '%s -a' % cmd
664                 cmd = '%s %s' % (cmd, file)
665         elif os.path.isdir(file):
666                 filesdir = os.path.realpath(bb.data.getVar("FILESDIR", data, 1))
667                 destdir = "."
668                 if file[0:len(filesdir)] == filesdir:
669                         destdir = file[len(filesdir):file.rfind('/')]
670                         destdir = destdir.strip('/')
671                         if len(destdir) < 1:
672                                 destdir = "."
673                         elif not os.access("%s/%s" % (os.getcwd(), destdir), os.F_OK):
674                                 os.makedirs("%s/%s" % (os.getcwd(), destdir))
675                 cmd = 'cp -pPR %s %s/%s/' % (file, os.getcwd(), destdir)
676         else:
677                 (type, host, path, user, pswd, parm) = bb.decodeurl(url)
678                 if not 'patch' in parm:
679                         # The "destdir" handling was specifically done for FILESPATH
680                         # items.  So, only do so for file:// entries.
681                         if type == "file":
682                                 destdir = bb.decodeurl(url)[1] or "."
683                         else:
684                                 destdir = "."
685                         bb.mkdirhier("%s/%s" % (os.getcwd(), destdir))
686                         cmd = 'cp %s %s/%s/' % (file, os.getcwd(), destdir)
687
688         if not cmd:
689                 return True
690
691         dest = os.path.join(os.getcwd(), os.path.basename(file))
692         if os.path.exists(dest):
693                 if os.path.samefile(file, dest):
694                         return True
695
696         cmd = "PATH=\"%s\" %s" % (bb.data.getVar('PATH', data, 1), cmd)
697         bb.note("Unpacking %s to %s/" % (file, os.getcwd()))
698         ret = os.system(cmd)
699         return ret == 0
700
701 addtask unpack after do_fetch
702 do_unpack[dirs] = "${WORKDIR}"
703 python base_do_unpack() {
704         import re, os
705
706         localdata = bb.data.createCopy(d)
707         bb.data.update_data(localdata)
708
709         src_uri = bb.data.getVar('SRC_URI', localdata)
710         if not src_uri:
711                 return
712         src_uri = bb.data.expand(src_uri, localdata)
713         for url in src_uri.split():
714                 try:
715                         local = bb.data.expand(bb.fetch.localpath(url, localdata), localdata)
716                 except bb.MalformedUrl, e:
717                         raise FuncFailed('Unable to generate local path for malformed uri: %s' % e)
718                 local = os.path.realpath(local)
719                 ret = oe_unpack_file(local, localdata, url)
720                 if not ret:
721                         raise bb.build.FuncFailed()
722 }
723
724 def base_get_scmbasepath(d):
725         import bb
726         path_to_bbfiles = bb.data.getVar( 'BBFILES', d, 1 ).split()
727         return path_to_bbfiles[0][:path_to_bbfiles[0].rindex( "packages" )]
728
729 def base_get_metadata_monotone_branch(d):
730         monotone_branch = "<unknown>"
731         try:
732                 monotone_branch = file( "%s/_MTN/options" % base_get_scmbasepath(d) ).read().strip()
733                 if monotone_branch.startswith( "database" ):
734                         monotone_branch_words = monotone_branch.split()
735                         monotone_branch = monotone_branch_words[ monotone_branch_words.index( "branch" )+1][1:-1]
736         except:
737                 pass
738         return monotone_branch
739
740 def base_get_metadata_monotone_revision(d):
741         monotone_revision = "<unknown>"
742         try:
743                 monotone_revision = file( "%s/_MTN/revision" % base_get_scmbasepath(d) ).read().strip()
744                 if monotone_revision.startswith( "format_version" ):
745                         monotone_revision_words = monotone_revision.split()
746                         monotone_revision = monotone_revision_words[ monotone_revision_words.index( "old_revision" )+1][1:-1]
747         except IOError:
748                 pass
749         return monotone_revision
750
751 def base_get_metadata_svn_revision(d):
752         revision = "<unknown>"
753         try:
754                 revision = file( "%s/.svn/entries" % base_get_scmbasepath(d) ).readlines()[3].strip()
755         except IOError:
756                 pass
757         return revision
758
759 def base_get_metadata_git_branch(d):
760         import os
761         branch = os.popen('cd %s; git-branch | grep "^* " | tr -d "* "' % base_get_scmbasepath(d)).read()
762
763         if len(branch) != 0:
764                 return branch
765         return "<unknown>"
766
767 def base_get_metadata_git_revision(d):
768         import os
769         rev = os.popen("cd %s; git-log -n 1 --pretty=oneline --" % base_get_scmbasepath(d)).read().split(" ")[0]
770         if len(rev) != 0:
771                 return rev
772         return "<unknown>"
773
774 def base_detect_revision(d):
775         scms = [base_get_metadata_monotone_revision, \
776                         base_get_metadata_svn_revision, \
777                         base_get_metadata_git_revision]
778
779         for scm in scms:
780                 rev = scm(d)
781                 if rev <> "<unknown>":
782                         return rev
783
784         return "<unknown>"      
785
786 def base_detect_branch(d):
787         scms = [base_get_metadata_monotone_branch, \
788                         base_get_metadata_git_branch]
789
790         for scm in scms:
791                 rev = scm(d)
792                 if rev <> "<unknown>":
793                         return rev.strip()
794
795         return "<unknown>"      
796         
797         
798
799 METADATA_BRANCH ?= "${@base_detect_branch(d)}"
800 METADATA_REVISION ?= "${@base_detect_revision(d)}"
801
802 addhandler base_eventhandler
803 python base_eventhandler() {
804         from bb import note, error, data
805         from bb.event import Handled, NotHandled, getName
806         import os
807
808         messages = {}
809         messages["Completed"] = "completed"
810         messages["Succeeded"] = "completed"
811         messages["Started"] = "started"
812         messages["Failed"] = "failed"
813
814         name = getName(e)
815         msg = ""
816         if name.startswith("Pkg"):
817                 msg += "package %s: " % data.getVar("P", e.data, 1)
818                 msg += messages.get(name[3:]) or name[3:]
819         elif name.startswith("Task"):
820                 msg += "package %s: task %s: " % (data.getVar("PF", e.data, 1), e.task)
821                 msg += messages.get(name[4:]) or name[4:]
822         elif name.startswith("Build"):
823                 msg += "build %s: " % e.name
824                 msg += messages.get(name[5:]) or name[5:]
825         elif name == "UnsatisfiedDep":
826                 msg += "package %s: dependency %s %s" % (e.pkg, e.dep, name[:-3].lower())
827         if msg:
828                 note(msg)
829
830         if name.startswith("BuildStarted"):
831                 bb.data.setVar( 'BB_VERSION', bb.__version__, e.data )
832                 statusvars = ['BB_VERSION', 'METADATA_BRANCH', 'METADATA_REVISION', 'TARGET_ARCH', 'TARGET_OS', 'MACHINE', 'DISTRO', 'DISTRO_VERSION','TARGET_FPU']
833                 statuslines = ["%-17s = \"%s\"" % (i, bb.data.getVar(i, e.data, 1) or '') for i in statusvars]
834                 statusmsg = "\nOE Build Configuration:\n%s\n" % '\n'.join(statuslines)
835                 print statusmsg
836
837                 needed_vars = [ "TARGET_ARCH", "TARGET_OS" ]
838                 pesteruser = []
839                 for v in needed_vars:
840                         val = bb.data.getVar(v, e.data, 1)
841                         if not val or val == 'INVALID':
842                                 pesteruser.append(v)
843                 if pesteruser:
844                         bb.fatal('The following variable(s) were not set: %s\nPlease set them directly, or choose a MACHINE or DISTRO that sets them.' % ', '.join(pesteruser))
845
846         #
847         # Handle removing stamps for 'rebuild' task
848         #
849         if name.startswith("StampUpdate"):
850                 for (fn, task) in e.targets:
851                         #print "%s %s" % (task, fn)         
852                         if task == "do_rebuild":
853                                 dir = "%s.*" % e.stampPrefix[fn]
854                                 bb.note("Removing stamps: " + dir)
855                                 os.system('rm -f '+ dir)
856                                 os.system('touch ' + e.stampPrefix[fn] + '.needclean')
857
858         if not data in e.__dict__:
859                 return NotHandled
860
861         log = data.getVar("EVENTLOG", e.data, 1)
862         if log:
863                 logfile = file(log, "a")
864                 logfile.write("%s\n" % msg)
865                 logfile.close()
866
867         return NotHandled
868 }
869
870 addtask configure after do_unpack do_patch
871 do_configure[dirs] = "${S} ${B}"
872 do_configure[deptask] = "do_populate_staging"
873 base_do_configure() {
874         :
875 }
876
877 addtask compile after do_configure
878 do_compile[dirs] = "${S} ${B}"
879 base_do_compile() {
880         if [ -e Makefile -o -e makefile ]; then
881                 oe_runmake || die "make failed"
882         else
883                 oenote "nothing to compile"
884         fi
885 }
886
887 base_do_stage () {
888         :
889 }
890
891 do_populate_staging[dirs] = "${STAGING_DIR_TARGET}/${layout_bindir} ${STAGING_DIR_TARGET}/${layout_libdir} \
892                              ${STAGING_DIR_TARGET}/${layout_includedir} \
893                              ${STAGING_BINDIR_NATIVE} ${STAGING_LIBDIR_NATIVE} \
894                              ${STAGING_INCDIR_NATIVE} \
895                              ${STAGING_DATADIR} \
896                              ${S} ${B}"
897
898 # Could be compile but populate_staging and do_install shouldn't run at the same time
899 addtask populate_staging after do_install
900
901 python do_populate_staging () {
902     bb.build.exec_func('do_stage', d)
903 }
904
905 addtask install after do_compile
906 do_install[dirs] = "${D} ${S} ${B}"
907 # Remove and re-create ${D} so that is it guaranteed to be empty
908 do_install[cleandirs] = "${D}"
909
910 base_do_install() {
911         :
912 }
913
914 base_do_package() {
915         :
916 }
917
918 addtask build after do_populate_staging
919 do_build = ""
920 do_build[func] = "1"
921
922 # Functions that update metadata based on files outputted
923 # during the build process.
924
925 def explode_deps(s):
926         r = []
927         l = s.split()
928         flag = False
929         for i in l:
930                 if i[0] == '(':
931                         flag = True
932                         j = []
933                 if flag:
934                         j.append(i)
935                         if i.endswith(')'):
936                                 flag = False
937                                 r[-1] += ' ' + ' '.join(j)
938                 else:
939                         r.append(i)
940         return r
941
942 def packaged(pkg, d):
943         import os, bb
944         return os.access(get_subpkgedata_fn(pkg, d) + '.packaged', os.R_OK)
945
946 def read_pkgdatafile(fn):
947         pkgdata = {}
948
949         def decode(str):
950                 import codecs
951                 c = codecs.getdecoder("string_escape")
952                 return c(str)[0]
953
954         import os
955         if os.access(fn, os.R_OK):
956                 import re
957                 f = file(fn, 'r')
958                 lines = f.readlines()
959                 f.close()
960                 r = re.compile("([^:]+):\s*(.*)")
961                 for l in lines:
962                         m = r.match(l)
963                         if m:
964                                 pkgdata[m.group(1)] = decode(m.group(2))
965
966         return pkgdata
967
968 def get_subpkgedata_fn(pkg, d):
969         import bb, os
970         archs = bb.data.expand("${PACKAGE_ARCHS}", d).split(" ")
971         archs.reverse()
972         pkgdata = bb.data.expand('${TMPDIR}/pkgdata/', d)
973         targetdir = bb.data.expand('${TARGET_VENDOR}-${TARGET_OS}/runtime/', d)
974         for arch in archs:
975                 fn = pkgdata + arch + targetdir + pkg
976                 if os.path.exists(fn):
977                         return fn
978         return bb.data.expand('${PKGDATA_DIR}/runtime/%s' % pkg, d)
979
980 def has_subpkgdata(pkg, d):
981         import bb, os
982         return os.access(get_subpkgedata_fn(pkg, d), os.R_OK)
983
984 def read_subpkgdata(pkg, d):
985         import bb
986         return read_pkgdatafile(get_subpkgedata_fn(pkg, d))
987
988 def has_pkgdata(pn, d):
989         import bb, os
990         fn = bb.data.expand('${PKGDATA_DIR}/%s' % pn, d)
991         return os.access(fn, os.R_OK)
992
993 def read_pkgdata(pn, d):
994         import bb
995         fn = bb.data.expand('${PKGDATA_DIR}/%s' % pn, d)
996         return read_pkgdatafile(fn)
997
998 python read_subpackage_metadata () {
999         import bb
1000         data = read_pkgdata(bb.data.getVar('PN', d, 1), d)
1001
1002         for key in data.keys():
1003                 bb.data.setVar(key, data[key], d)
1004
1005         for pkg in bb.data.getVar('PACKAGES', d, 1).split():
1006                 sdata = read_subpkgdata(pkg, d)
1007                 for key in sdata.keys():
1008                         bb.data.setVar(key, sdata[key], d)
1009 }
1010
1011 # Make sure MACHINE isn't exported
1012 # (breaks binutils at least)
1013 MACHINE[unexport] = "1"
1014
1015 # Make sure TARGET_ARCH isn't exported
1016 # (breaks Makefiles using implicit rules, e.g. quilt, as GNU make has this 
1017 # in them, undocumented)
1018 TARGET_ARCH[unexport] = "1"
1019
1020 # Make sure DISTRO isn't exported
1021 # (breaks sysvinit at least)
1022 DISTRO[unexport] = "1"
1023
1024
1025 def base_after_parse(d):
1026     import bb, os, exceptions
1027
1028     source_mirror_fetch = bb.data.getVar('SOURCE_MIRROR_FETCH', d, 0)
1029     if not source_mirror_fetch:
1030         need_host = bb.data.getVar('COMPATIBLE_HOST', d, 1)
1031         if need_host:
1032             import re
1033             this_host = bb.data.getVar('HOST_SYS', d, 1)
1034             if not re.match(need_host, this_host):
1035                 raise bb.parse.SkipPackage("incompatible with host %s" % this_host)
1036
1037         need_machine = bb.data.getVar('COMPATIBLE_MACHINE', d, 1)
1038         if need_machine:
1039             import re
1040             this_machine = bb.data.getVar('MACHINE', d, 1)
1041             if this_machine and not re.match(need_machine, this_machine):
1042                 raise bb.parse.SkipPackage("incompatible with machine %s" % this_machine)
1043
1044     pn = bb.data.getVar('PN', d, 1)
1045
1046     # OBSOLETE in bitbake 1.7.4
1047     srcdate = bb.data.getVar('SRCDATE_%s' % pn, d, 1)
1048     if srcdate != None:
1049         bb.data.setVar('SRCDATE', srcdate, d)
1050
1051     use_nls = bb.data.getVar('USE_NLS_%s' % pn, d, 1)
1052     if use_nls != None:
1053         bb.data.setVar('USE_NLS', use_nls, d)
1054
1055     # Git packages should DEPEND on git-native
1056     srcuri = bb.data.getVar('SRC_URI', d, 1)
1057     if "git://" in srcuri:
1058         depends = bb.data.getVarFlag('do_fetch', 'depends', d) or ""
1059         depends = depends + " git-native:do_populate_staging"
1060         bb.data.setVarFlag('do_fetch', 'depends', depends, d)
1061
1062     # 'multimachine' handling
1063     mach_arch = bb.data.getVar('MACHINE_ARCH', d, 1)
1064     pkg_arch = bb.data.getVar('PACKAGE_ARCH', d, 1)
1065
1066     if (pkg_arch == mach_arch):
1067         # Already machine specific - nothing further to do
1068         return
1069
1070     #
1071     # We always try to scan SRC_URI for urls with machine overrides
1072     # unless the package sets SRC_URI_OVERRIDES_PACKAGE_ARCH=0
1073     #
1074     override = bb.data.getVar('SRC_URI_OVERRIDES_PACKAGE_ARCH', d, 1)
1075     if override != '0':
1076         paths = []
1077         for p in [ "${PF}", "${P}", "${PN}", "files", "" ]:
1078             path = bb.data.expand(os.path.join("${FILE_DIRNAME}", p, "${MACHINE}"), d)
1079             if os.path.isdir(path):
1080                 paths.append(path)
1081         if len(paths) != 0:
1082             for s in srcuri.split():
1083                 if not s.startswith("file://"):
1084                     continue
1085                 local = bb.data.expand(bb.fetch.localpath(s, d), d)
1086                 for mp in paths:
1087                     if local.startswith(mp):
1088                         #bb.note("overriding PACKAGE_ARCH from %s to %s" % (pkg_arch, mach_arch))
1089                         bb.data.setVar('PACKAGE_ARCH', "${MACHINE_ARCH}", d)
1090                         bb.data.setVar('MULTIMACH_ARCH', mach_arch, d)
1091                         return
1092
1093     multiarch = pkg_arch
1094
1095     packages = bb.data.getVar('PACKAGES', d, 1).split()
1096     for pkg in packages:
1097         pkgarch = bb.data.getVar("PACKAGE_ARCH_%s" % pkg, d, 1)
1098
1099         # We could look for != PACKAGE_ARCH here but how to choose 
1100         # if multiple differences are present?
1101         # Look through PACKAGE_ARCHS for the priority order?
1102         if pkgarch and pkgarch == mach_arch:
1103             multiarch = mach_arch
1104             break
1105
1106     bb.data.setVar('MULTIMACH_ARCH', multiarch, d)
1107
1108 python () {
1109     import bb
1110     from bb import __version__
1111     base_after_parse(d)
1112
1113     # Remove this for bitbake 1.8.12
1114     try:
1115         from distutils.version import LooseVersion
1116     except ImportError:
1117         def LooseVersion(v): print "WARNING: sanity.bbclass can't compare versions without python-distutils"; return 1
1118     if (LooseVersion(__version__) >= LooseVersion('1.8.11')):
1119         deps = bb.data.getVarFlag('do_rebuild', 'deps', d) or []
1120         deps.append('do_' + bb.data.getVar('BB_DEFAULT_TASK', d, 1))
1121         bb.data.setVarFlag('do_rebuild', 'deps', deps, d)
1122 }
1123
1124 def check_app_exists(app, d):
1125         from bb import which, data
1126
1127         app = data.expand(app, d)
1128         path = data.getVar('PATH', d, 1)
1129         return len(which(path, app)) != 0
1130
1131 def check_gcc3(data):
1132
1133         gcc3_versions = 'gcc-3.4 gcc34 gcc-3.4.4 gcc-3.4.6 gcc-3.4.7 gcc-3.3 gcc33 gcc-3.3.6 gcc-3.2 gcc32'
1134
1135         for gcc3 in gcc3_versions.split():
1136                 if check_app_exists(gcc3, data):
1137                         return gcc3
1138         
1139         return False
1140
1141 # Patch handling
1142 inherit patch
1143
1144 # Configuration data from site files
1145 # Move to autotools.bbclass?
1146 inherit siteinfo
1147
1148 EXPORT_FUNCTIONS do_setscene do_clean do_mrproper do_fetch do_unpack do_configure do_compile do_install do_package do_populate_pkgs do_stage do_rebuild do_fetchall
1149
1150 MIRRORS[func] = "0"
1151 MIRRORS () {
1152 ${DEBIAN_MIRROR}/main   http://snapshot.debian.net/archive/pool
1153 ${DEBIAN_MIRROR}        ftp://ftp.de.debian.org/debian/pool
1154 ${DEBIAN_MIRROR}        ftp://ftp.au.debian.org/debian/pool
1155 ${DEBIAN_MIRROR}        ftp://ftp.cl.debian.org/debian/pool
1156 ${DEBIAN_MIRROR}        ftp://ftp.hr.debian.org/debian/pool
1157 ${DEBIAN_MIRROR}        ftp://ftp.fi.debian.org/debian/pool
1158 ${DEBIAN_MIRROR}        ftp://ftp.hk.debian.org/debian/pool
1159 ${DEBIAN_MIRROR}        ftp://ftp.hu.debian.org/debian/pool
1160 ${DEBIAN_MIRROR}        ftp://ftp.ie.debian.org/debian/pool
1161 ${DEBIAN_MIRROR}        ftp://ftp.it.debian.org/debian/pool
1162 ${DEBIAN_MIRROR}        ftp://ftp.jp.debian.org/debian/pool
1163 ${DEBIAN_MIRROR}        ftp://ftp.no.debian.org/debian/pool
1164 ${DEBIAN_MIRROR}        ftp://ftp.pl.debian.org/debian/pool
1165 ${DEBIAN_MIRROR}        ftp://ftp.ro.debian.org/debian/pool
1166 ${DEBIAN_MIRROR}        ftp://ftp.si.debian.org/debian/pool
1167 ${DEBIAN_MIRROR}        ftp://ftp.es.debian.org/debian/pool
1168 ${DEBIAN_MIRROR}        ftp://ftp.se.debian.org/debian/pool
1169 ${DEBIAN_MIRROR}        ftp://ftp.tr.debian.org/debian/pool
1170 ${GNU_MIRROR}   ftp://mirrors.kernel.org/gnu
1171 ${GNU_MIRROR}   ftp://ftp.matrix.com.br/pub/gnu
1172 ${GNU_MIRROR}   ftp://ftp.cs.ubc.ca/mirror2/gnu
1173 ${GNU_MIRROR}   ftp://sunsite.ust.hk/pub/gnu
1174 ${GNU_MIRROR}   ftp://ftp.ayamura.org/pub/gnu
1175 ${KERNELORG_MIRROR}     http://www.kernel.org/pub
1176 ${KERNELORG_MIRROR}     ftp://ftp.us.kernel.org/pub
1177 ${KERNELORG_MIRROR}     ftp://ftp.uk.kernel.org/pub
1178 ${KERNELORG_MIRROR}     ftp://ftp.hk.kernel.org/pub
1179 ${KERNELORG_MIRROR}     ftp://ftp.au.kernel.org/pub
1180 ${KERNELORG_MIRROR}     ftp://ftp.jp.kernel.org/pub
1181 ftp://ftp.gnupg.org/gcrypt/     ftp://ftp.franken.de/pub/crypt/mirror/ftp.gnupg.org/gcrypt/
1182 ftp://ftp.gnupg.org/gcrypt/     ftp://ftp.surfnet.nl/pub/security/gnupg/
1183 ftp://ftp.gnupg.org/gcrypt/     http://gulus.USherbrooke.ca/pub/appl/GnuPG/
1184 ftp://dante.ctan.org/tex-archive ftp://ftp.fu-berlin.de/tex/CTAN
1185 ftp://dante.ctan.org/tex-archive http://sunsite.sut.ac.jp/pub/archives/ctan/
1186 ftp://dante.ctan.org/tex-archive http://ctan.unsw.edu.au/
1187 ftp://ftp.gnutls.org/pub/gnutls ftp://ftp.gnutls.org/pub/gnutls/
1188 ftp://ftp.gnutls.org/pub/gnutls ftp://ftp.gnupg.org/gcrypt/gnutls/
1189 ftp://ftp.gnutls.org/pub/gnutls http://www.mirrors.wiretapped.net/security/network-security/gnutls/
1190 ftp://ftp.gnutls.org/pub/gnutls ftp://ftp.mirrors.wiretapped.net/pub/security/network-security/gnutls/
1191 ftp://ftp.gnutls.org/pub/gnutls http://josefsson.org/gnutls/releases/
1192 http://ftp.info-zip.org/pub/infozip/src/ http://mirror.switch.ch/ftp/mirror/infozip/src/
1193 http://ftp.info-zip.org/pub/infozip/src/ ftp://sunsite.icm.edu.pl/pub/unix/archiving/info-zip/src/
1194 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://ftp.cerias.purdue.edu/pub/tools/unix/sysutils/lsof/
1195 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://ftp.tau.ac.il/pub/unix/admin/
1196 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://ftp.cert.dfn.de/pub/tools/admin/lsof/
1197 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://ftp.fu-berlin.de/pub/unix/tools/lsof/
1198 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://ftp.kaizo.org/pub/lsof/
1199 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://ftp.tu-darmstadt.de/pub/sysadmin/lsof/
1200 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://ftp.tux.org/pub/sites/vic.cc.purdue.edu/tools/unix/lsof/
1201 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://gd.tuwien.ac.at/utils/admin-tools/lsof/
1202 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://sunsite.ualberta.ca/pub/Mirror/lsof/
1203 ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/  ftp://the.wiretapped.net/pub/security/host-security/lsof/
1204 http://www.apache.org/dist  http://archive.apache.org/dist
1205
1206 }
1207