File: mmake.in

package info (click to toggle)
mmake 2.2.1-2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 224 kB
  • ctags: 75
  • sloc: perl: 304; makefile: 242; sh: 169; java: 17
file content (718 lines) | stat: -rwxr-xr-x 19,398 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
#!@PERL@ -w
#
# (c) 1998-2001 Jan-Henrik Haukeland, <hauk@tildeslash.com>
#

=head1 NAME

 mmake - generate a Java Makefile

=head1 SYNOPSIS

 mmake [ -d | -v ]

=head1 DESCRIPTION

This program will generate a Makefile for Java source files. Use the
I<-d> option to accept all defaults.

After running mmake, you will obtain a Makefile in the directory from
where you started the program. The Makefile will handle java files in
the current directory and in any sub-directories.

Use the generated Makefile with mmake as follows:

To compile Java files just type B<make>. It's also possible to run
make with one of the following targets: I<doc, clean, help, jar,
install, uninstall, tags and depend> Where 'make doc' runs javadoc on
the source files, it will only work for files in a package. The
command 'make clean' removes class files and other temporary
files. The command 'make jar' creates a jar file with all class files
(and other files of your choice, see the JAR_OBJS variable in the
Makefile). The command 'make install' will install a jar file, class
files and any shell wrappers you have made. (A shell script must have
the extension .sh to be installed). Use 'make uninstall' to remove
installed files. The command 'make help', shows a help text with
available targets. The command 'make tags' will generate a tag file
for Emacs. And finally the command 'make depend' creates a dependency
graph for the class files. (The dependency graph will be put in a file
called I<makefile.dep>, which is included in the Makefile)

You don't have to run mmake each time you add a new java file to your
project. You can add as many new java files as you like, the Makefile
will find them. This is the case as long as you don't add a new
package. In that case, you must either run mmake again or update the
PACKAGE variable in the Makefile. This is because the Makefile uses
this variable to find directories with java files.

The program mmake is able to create a dependency graph for your java
files. To do this, it needs the I<jikes> compiler from IBM. Get jikes
from B<http://www.ibm.com/java/tools>. You would probably be more
content with jikes anyhow, since it is much faster than javac. To
create a dependencies graph, do a I<make clean> before running I<make
depend>.


=head1 A NOTE ON INSTALLATION

The Makefile created with mmake will do a fair job installing the
different files that makes up your system. It uses the following
Makefile variables when it conducts the install routine:

=over 4

=item * 

PREFIX

=item * 

CLASS_DIR

=item * 

JAR_DIR

=item * 

DOC_DIR

=item * 

SCRIPT_DIR

=back

=head2 PREFIX

This variable will be prepended to all other directory variables
above. It is used for grouping the other directories into one root
directory. If you don't want that, you may simply set the variable to
an empty string in the Makefile. If the variable is empty you could
still use it on the command line when you run make, for instance for a
one-shoot installation like: B<make PREFIX=/local/myproject/ install>

=head2 CLASS_DIR

This variable denotes the top directory from where all class files
will be installed. Its default value is B<classes>, which I believe is
a good value. B<Note:> If you I<don't> want to install any class files
(because you are, for example, only going to use a jar file), set this
variable to an empty string and no class files will be installed. 

Resource files will also be installed below this directory if such
files are present in a package structure. This is useful if you are
using e.g. ResourceBundles to Localize your application and have your
property files in it's own directory in the package structure.

=head2 JAR_DIR

This variable tells the Makefile where to install the jar file. The
default value is B<lib>, which is also a good default value.

=head2 DOC_DIR

When you run javadoc, all the html files will be put into this
directory. Its default value is B<doc/api-docs>. You should probably
keep that name, but then again, you may change it as you like.

=head2 SCRIPT_DIR

The Makefile uses this variable to install any shell wrapper-scripts
that you have created. If you write an application, it is always nice
for the user that you provide a wrapper script to start the
application. Its default value is B<bin>. (The Makefile will only
install shell-scripts that has the extension .sh. The mmake script
will tell the Makefile where to look for shell-scripts)


=head2 INSTALLATION SUMMARY

If you keep the default values you will get an installation tree that
looks like this:

 `-- PREFIX
     |-- bin
     |-- classes
     |   `-- package <--- Example of a sub-directory
     |       |-- sub-package1
     |       |-- sub-package2
     |       `-- sub-package3
     |-- doc
     |   `-- api-docs
     `-- lib


=head1 USING THE C-PREPROCESSOR

This is a excellent tool for managing projects with several different
versions. The idea behind using the C preprocessor with Java is to
better manage different versions more easily. This is done by using
CPP conditional statements in the source files. I would strongly
advise you not to use CPP to redefine the Java language itself.

To use the C preprocessor together with Java, you can change the name
of the source files that you want to preprocess -- from
<filename>.java to <filename>.xjava. The Makefile has a rule to build
.class files from .xjava files.

It is B<not> necesarry to change every file from .java to .xjava. The
Makefile will work well and consistently in an environment of both
.java and .xjava files. (E.g. 'make clean' will only remove .java
files that were created from a .xjava file. Other java files will, of
course, I<not> be removed.)

You can now use cpp Conditionals in Your Java-code, for example, as
follows:

    #ifdef JAVA1_1
       [code1]
    #else
       [code2]
    #endif

The JAVA1_1 label in the above example is tested against the
VERSION variable in the Makefile. That is, if the VERSION variable is
JAVA1_1, then [code1] would be compiled and [code2] left out. Likewise,
if VERSION is something else than JAVA1_1, then [code2] would be compiled and
[code1] left out of the resulting .class file.

=head1 NOTES

mmake will give you I<one> Makefile for managing your Java files.
Although it's easy to setup and use mmake in a recursive makefile
context, you don't want to do that. To see why, read the excellent
article: B<Recursive Make Considered Harmful> at
I<http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html>

=head1 DEPENDENCIES

mmake will need the following:

=over 4

=item * 

Perl 5.x

=item * 

Gnu make

=item * 

Gnu xargs (recommended)

=back

=head1 AUTHOR

Jan-Henrik Haukeland <hauk@tildeslash.com>

=cut

use strict;
use vars qw($opt_d $opt_v);
use Getopt::Std;
require 5.000;                   # Need this perl version at least

# Prototypes
sub getline($$);
sub getdirline($$);
sub getpreviewline($$$);
sub do_get();
sub do_find($$);


my $REVISION= sprintf("%d.%02d", q$Revision: 1.8 $ =~ /(\d+)\.(\d+)/);

my $VERSION= "2.2.1";            # The program version
(my $PROG = $0)=~   s,.*/,,;     # This Program name (usually 'mmake')
my $M=             "Makefile";   # The Java Makefile
my @packages=      ();           # Array holding packages, i.e. subdirectories 
my @scripts=       ();           # Array holding shell-script directories 
my @resources=     ();           # Array holding resource files
my $toplevel=      "";           # Defined if toplevel java files

# Parse command line options
getopts("dv") || die "Usage: $PROG [ -d | -v ]\n";

if ( defined $opt_v ) {
  print "This is mmake, version $VERSION\n";
  exit(0);
}


# ---------------------
# Assign macro defaults
# ---------------------
my $javac=        "@JAVAC@"; 
my $jflags=       "";
my $javadoc=      "javadoc";
my $jdocflags=    "-version -author";
my $jar=          "jar";
my $jarflags=     "cvf0";
my $jarfile=      "";
my $prefixdir=    "";
my $docdir=       "doc/api-docs";
my $jardir=       "lib";
my $classdir=     "classes";
my $bindir=       "bin";
my $cpp=          "cpp";
my $cppflags=     "-C -P";


if (-t and ( !$opt_d )) {
    # Let the user override the defaults
    print "Give Makefile variables or just type [enter] for default\n\n";
    $javac=         getline("JAVAC",   $javac);
    $jflags=        getline("JAVAC flags", $jflags);
    $javadoc=       getline("JAVADOC", $javadoc);
    $jdocflags=     getline("JAVADOC flags", $jdocflags);
    $jar=           getline("JAR", $jar);
    $jarflags=      getline("JAR flags", $jarflags);
    $jarfile=       getline("JAR File name (e.g. foobar.jar)", $jarfile);

    $prefixdir=     getdirline("PREFIX dir. (Will be prepended to other ".
			       "install dir)",  $prefixdir);
    $docdir=        getpreviewline("INSTALL dir. for javadoc html-files.",
				   "$prefixdir$docdir", $docdir);
    $classdir=      getpreviewline("INSTALL dir. for class files", 
				   "$prefixdir$classdir", $classdir);
    $bindir=        getpreviewline("INSTALL dir. for shell-scripts",
				   "$prefixdir$bindir", $bindir);
    $jardir=        getpreviewline("INSTALL dir. for the jar file ",
				   "$prefixdir$jardir", $jardir);

    if( getline("Use CPP [y|n] ?", "no") =~ /^y/i ) {
	$cpp=      getline("CPP", $cpp);
	$cppflags= getline("CPP flags", $cppflags);
    }

    print "\n";
}


# Locate the java files/packages
do_get();
die "No java source files found\n" unless @packages or $toplevel;

# If an old Makefile exists, rename it
if (-f $M) {
    rename($M, "$M.old") or
      die "$PROG: Cannot rename local Makefile.\n";
}

# Then create the new makefile
open(MAKEFILE, ">$M") || die "$PROG: Cannot create '$M': $!\n";


print MAKEFILE <<EOT;
#
# Makefile created at @{[scalar localtime]}, by $PROG
#

# Programs (with common options):
SHELL		= /bin/sh
RM              = rm -f
MV              = mv -f
SED		= sed
ETAGS		= etags
XARGS		= xargs
CAT		= cat
FIND            = find
CPP		= $cpp $cppflags

INSTALL         = install
INSTALL_PROG    = \$(INSTALL) -m \$(MODE_PROGS)
INSTALL_FILE    = \$(INSTALL) -m \$(MODE_FILES)
INSTALL_DIR     = \$(INSTALL) -m \$(MODE_DIRS) -d

# Install modes 
MODE_PROGS      = 555
MODE_FILES      = 444
MODE_DIRS       = 2755

# Build programs
JAVAC           = $javac
JAVADOC         = $javadoc
JAR             = $jar

# Build flags
JAVAC_FLAGS     = $jflags
JAVADOC_FLAGS   = $jdocflags
JAR_FLAGS       = $jarflags
JIKES_DEP_FLAG	= +M

# ------------------------------------------------------------------- #

# Prefix for every install directory
PREFIX		= $prefixdir

# Where to start installing the class files. Set this to an empty value
#  if you dont want to install classes
CLASS_DIR	= \$(PREFIX)$classdir

# The directory to install the jar file in. Set this to an empty value
#  if you dont want to install a jar file
JAR_DIR	        = \$(PREFIX)$jardir

# The directory to install html files generated by javadoc
DOC_DIR         = \$(PREFIX)$docdir

# The directory to install script files in
SCRIPT_DIR	= \$(PREFIX)$bindir

# ------------------------------------------------------------------- #

# The name of the jar file to install
JAR_FILE        = $jarfile

# 
# The VERSION variable below should be set to a value 
# that will be tested in the .xjava code. 
# 
VERSION		= CHANGE_ME

# ------------------------------------------------------------------- #

EOT


# Print the packages 
print MAKEFILE "
# Packages we should compile
PACKAGES = ",
  @packages ? ("\\\n\t" . join(" \\\n\t", @packages)) : "",
  "\n\n";

# Print the resource "packages"
print MAKEFILE "
# Resource packages
RESOURCES = ",
  @resources ? ("\\\n\t" . join(" \\\n\t", @resources)) : "",
  "\n\n";

# Print the script dir list
print MAKEFILE "
# Directories with shell scripts
SCRIPTS = ",
  @scripts ? ("\\\n\t" . join(" \\\n\t", @scripts)) : "",
  "\n\n";


print MAKEFILE <<EOT;
# ------------------------------------------------------------------- #

# A marker variable for the top level directory
TOPLEVEL	:= .

# Subdirectories with java files:
JAVA_DIRS	:= \$(subst .,/,\$(PACKAGES)) \$(TOPLEVEL)

# Subdirectories with only resource files:
RESOURCE_DIRS	:= \$(subst .,/,\$(RESOURCES))

# All the .xjava source files:
XJAVA_SRC	:= \$(foreach dir, \$(JAVA_DIRS), \$(wildcard \$(dir)/*.xjava))

# All the xjava files to build
XJAVA_OBJS	:= \$(XJAVA_SRC:.xjava=.java)

# All the .java source files:
JAVA_SRC	:= \$(foreach dir, \$(JAVA_DIRS), \$(wildcard \$(dir)/*.java))
JAVA_SRC	:= \$(XJAVA_OBJS) \$(JAVA_SRC)

# Dependency files:
DEPEND_OBJS	:= \$(JAVA_SRC:.java=.u)

# Objects that should go into the jar file. (find syntax)
JAR_OBJS	:= \\( -name '*.class' -o -name '*.gif' -o -name "*.au" \\
		       -o -name '*.properties' \\)

# The intermediate java files and main classes we should build:
JAVA_OBJS	:= \$(XJAVA_OBJS) \$(JAVA_SRC:.java=.class)

# Resource files:
#  Extend the list to install other files of your choice
RESOURCE_SRC	:= *.properties *.gif *.au
#  Search for resource files in both JAVA_DIRS and RESOURCE_DIRS
RESOURCE_OBJS	:= \$(foreach dir, \$(JAVA_DIRS) \$(RESOURCE_DIRS), \\
		     \$(wildcard \$(foreach file, \$(RESOURCE_SRC), \\
		     \$(dir)/\$(file))))

# All the shell scripts source
SCRIPT_SRCS 	:= \$(foreach dir, \$(SCRIPTS), \$(wildcard \$(dir)/*.sh))
# All shell scripts we should install
SCRIPT_OBJS    	:= \$(SCRIPT_SRCS:.sh=)

# All the files to install into CLASS_DIR
INSTALL_OBJS	:= \$(foreach dir, \$(JAVA_DIRS), \$(wildcard \$(dir)/*.class))
# Escape inner class delimiter \$
INSTALL_OBJS	:= \$(subst \$\$,\\\$\$,\$(INSTALL_OBJS))
# Add the resource files to be installed as well
INSTALL_OBJS	:= \$(INSTALL_OBJS) \$(RESOURCE_OBJS)


# ------------------------------------------------------------------- #


define check-exit
|| exit 1

endef


# -----------
# Build Rules
# -----------

%.java: %.xjava
	\$(CPP) -D\$(VERSION) \$< \$@

%.class: %.java
	\$(JAVAC) \$(JAVAC_FLAGS) \$<

%.jar: \$(JAVA_OBJS) \$(RESOURCE_OBJS)
	\$(FIND) \$(TOPLEVEL) \$(JAR_OBJS) -print | \$(XARGS) \\
	\$(JAR) \$(JAR_FLAGS) \$(JAR_FILE) 

%.u: %.java
	\$(JAVAC) \$(JIKES_DEP_FLAG) \$<


# -------
# Targets
# -------

.PHONY: all jar install uninstall doc clean depend tags

all::	\$(JAVA_OBJS)


help:
	\@echo "Usage: make {all|jar|install|uninstall|doc|clean|depend|tags}"


# Jar target
ifneq (\$(strip \$(JAR_FILE)),)
jar:  \$(JAR_FILE)
ifneq (\$(strip \$(JAR_DIR)),)
install:: \$(JAR_FILE)
	\@echo "===> [Installing jar file, \$(JAR_FILE) in \$(JAR_DIR)] "
	\$(INSTALL_DIR) \$(JAR_DIR) \$(check-exit)
	\$(INSTALL_FILE) \$(JAR_FILE) \$(JAR_DIR) \$(check-exit)
uninstall::
	\@echo "===> [Removing jar file, \$(JAR_FILE) from \$(JAR_DIR)] "
	\$(RM) \$(JAR_DIR)/\$(JAR_FILE)  \$(check-exit)
else
install::
	\@echo "No jar install dir defined"
endif
clean::
	\$(RM) \$(JAR_FILE)
else
jar:
	\@echo "No jar file defined"
endif



# Install target for Classes and Resources 
ifneq (\$(strip \$(CLASS_DIR)),)
install:: \$(JAVA_OBJS)
	\@echo "===> [Installing classes in \$(CLASS_DIR)] "
	\$(INSTALL_DIR) \$(CLASS_DIR) \$(check-exit)
	\$(foreach dir, \$(JAVA_DIRS) \$(RESOURCE_DIRS), \\
		\$(INSTALL_DIR) \$(CLASS_DIR)/\$(dir) \$(check-exit))
	\$(foreach file, \$(INSTALL_OBJS), \\
		\$(INSTALL_FILE) \$(file) \$(CLASS_DIR)/\$(file) \\
	\$(check-exit))
uninstall::
	\@echo "===> [Removing class-files from \$(CLASS_DIR)] "
	\$(foreach file, \$(INSTALL_OBJS), \\
		\$(RM) \$(CLASS_DIR)/\$(file) \\
	\$(check-exit))
else
# Print a warning here if you like. (No class install dir defined)
endif



# Depend target
ifeq (\$(findstring jikes,\$(JAVAC)),jikes)
depend: \$(XJAVA_OBJS) \$(DEPEND_OBJS)
	( \$(CAT) \$(DEPEND_OBJS) |  \$(SED) -e '/\\.class\$\$/d' \\
	  -e '/.*\$\$.*/d' > \$(MAKEFILE_DEPEND); \$(RM) \$(DEPEND_OBJS); )
else
depend:
	\@echo "mmake needs the jikes compiler to build class dependencies"
endif



# Doc target
ifneq (\$(strip \$(PACKAGES)),)
doc:	\$(JAVA_SRC)
	\@echo "===> [Installing java documentation in \$(DOC_DIR)] "
	\$(INSTALL_DIR) \$(DOC_DIR) \$(check-exit)
	\$(JAVADOC) -d \$(DOC_DIR) \$(JAVADOC_FLAGS) \$(PACKAGES)
else
doc:
	\@echo "You must put your source files in a package to run make doc"
endif



# Script target
ifneq (\$(strip  \$(SCRIPT_OBJS)),)
all::	 \$(SCRIPT_OBJS)
ifneq (\$(strip \$(SCRIPT_DIR)),)
install:: \$(SCRIPT_OBJS)
	\@echo "===> [Installing shell-scripts in \$(SCRIPT_DIR)] "
	\$(INSTALL_DIR) \$(SCRIPT_DIR) \$(check-exit)
	\$(foreach file, \$(SCRIPT_OBJS), \\
		\$(INSTALL_PROG) \$(file) \$(SCRIPT_DIR) \$(check-exit))
uninstall:: 
	\@echo "===> [Removing shell-scripts from \$(SCRIPT_DIR)] "
	\$(foreach file, \$(SCRIPT_OBJS), \\
		\$(RM) \$(SCRIPT_DIR)/\$(file) \$(check-exit))
else
# Print a warning here if you like. (No script install dir defined)
endif
clean::
	rm -f \$(SCRIPT_OBJS)
endif



# Tag target
tags:	
	\@echo "Tagging"
	\$(ETAGS) \$(filter-out \$(XJAVA_OBJS), \$(JAVA_SRC)) \$(XJAVA_SRC)



# Various cleanup routines
clean::
	\$(FIND) . \\( -name '*~' -o -name '*.class' \\) -print | \\
	\$(XARGS) \$(RM) 
	\$(FIND) . -name '*.u' -print | \$(XARGS) \$(RM)

ifneq (\$(strip \$(XJAVA_SRC)),)
clean::
	\$(RM) \$(XJAVA_OBJS)
endif

# ----------------------------------------
# Include the dependency graph if it exist
# ----------------------------------------
MAKEFILE_DEPEND	= makefile.dep
DEPEND	= \$(wildcard \$(MAKEFILE_DEPEND))
ifneq (\$(DEPEND),)
	include \$(MAKEFILE_DEPEND)
endif

EOT

close(MAKEFILE);

print "'$M' created\n";

exit(0);


# ------------------------------------------------------------------------- #
# ----------------------------- Subroutines ------------------------------- #
# ------------------------------------------------------------------------- #

sub getline($$)
{
  my $key= shift;
  my $dir= shift;
  my $j;
  print "$key [$dir]: ";
  chomp($j= <STDIN>);

  return ($j =~ /\w/ ? $j : $dir);
}


sub getdirline($$)
{
  my $key= shift;
  my $dir= shift;
  my $j;
  print "$key [$dir]: ";
  chomp($j= <STDIN>);
  if ( $j =~ /(\w)|(^\.)/ ) {
    $j .="/" if ( substr($j, -1) ne "/" );
    return $j;
  }

  return $dir;
}


sub getpreviewline($$$)
{
  my $key=     shift;
  my $preview= shift;
  my $dir=     shift;
  my $j;
  print "$key [$preview]: ";
  chomp($j= <STDIN>);

  return ($j =~ /\w/ ? $j : $dir);
}


sub do_get()
{
  my $cmd;

  # Get directories with java files
  $cmd= q$find . \( -name "*.java" -o -name "*.xjava" \) $
       .q$-print 2>/dev/null |$;
  @packages= do_find("p", $cmd);
  return unless @packages or $toplevel;

  # Get directories with shell-script files
  $cmd= q$find . \( -name "*.sh" \) -print 2>/dev/null |$;
  @scripts= do_find("s", $cmd);

  # Get directories with *only* resource files
  $cmd= q$find . \( -name "*.properties" -o -name "*.gif" -o -name $
       .q$"*.au" \) -print 2>/dev/null |$;
  # A hash with package values
  my %p= map { ($_), $_ } @packages;
  # Set only elements located in a package and not
  # already in the packages array
  @resources= grep { ! $p{$_} && $_ =~ /\./ } do_find("p", $cmd);
}


sub do_find($$)
{
  my $mode= shift; # p = use package syntax
  my $cmd= shift;
  my %unique= ();

  open(MYDIR, $cmd) or
    die "$PROG: Can't run the \"find(1)\" command: $!\n";

  # Get the directories
  while (<MYDIR>)
  {
    chomp;
    s,(.*)/.*,$1,;
    s,^./,,;
    s,/,.,g if ( $mode eq "p" );
    $unique{$_}= undef if $_ =~ /\w+/;
    $toplevel= "." if $_ =~ /\./;
  }
  close(MYDIR);

  return ( keys(%unique) ) ;
}