Make, classpaths and environment variables

Make, Ant and probably any other build language (and/or toolkit, since Ant is not much in the way of a language) are tricky bastards. I’ve been maintaining a parallel set of build scripts for a Java and C++ project for a few years now, and this has been great practice in getting my Make chops snappier. Just today, I found I needed to build the -Xbootclasspath argument for my makefile. I ended up re-learning an important lesson that applies to bash. Not expectedly so, since Make and bash are not of common ancestry…but that lesson was “spaces.” One needs to put at least one space after Makes conditionals if, ifeq or ifneq. Just like one has to put space after bash’s if, while, and for.

  • Bash:
    if [ -z "$classpath" ] ; then do
       classpath="./*jar"
    done
  • Make:
    ifeq (,$(classpath))
       classpath="./*jar"
    endif
      Both those statements are checking for zero length strings.

However, I’m going to stop comparing the two. You just look at where I’ve put those spaces, yung’un.
My real goal was to evaluate a series of conditions (is there an JAVA6_HOME defined? Is there a $JAVA_HOME/../jdk6 directory? Is there /usr/local/jdk6 directory? If so create a BOOTCLASSPATH variable:

This is the trick:

  4 comma:= ,
  5 colon:= :
  6 empty:=
  7 space:= $(empty) $(empty)
  ...
 56 ifneq (,$(JAVA6_HOME))
 57    ifneq (,$(wildcard  $(JAVA6_HOME)/.))
 58       JAVA6 = ${JAVA6_HOME}
 59    endif
 60 endif
 61 ifeq (,$(JAVA6))
 62    ifneq (,$(wildcard $(JAVA_HOME)/../jdk6/.))
 63       JAVA6 = $(JAVA_HOME)/../jdk6
 64    endif
 65 endif
 66 ifeq (,$(JAVA6))
 67    ifneq (,$(wildcard /usr/local/jdk6/.))
 68       JAVA6 = "/usr/local/jdk6"
 69    endif
 70 endif
 71 ifneq (,$(JAVA6))
 72    BOOTCLASSPATH := $(wildcard $(JAVA6)/lib/*.jar)
 73    JAVATARGET = -target 1.6 \
 74                -source 1.6 \
 75                -Xbootclasspath/p:$(subst $(space),$(colon),$(BOOTCLASSPATH))
 76    $(info BOOTCLASSPATH is $(BOOTCLASSPATH))                                                                     
 77    $(info JAVATARGET    is $(JAVATARGET))
 78 endif

And the quiz for the reader is, why is line 75 important?

Backups: Using `find` Across a Panalopy of Directories

Linux Backups logo

Linux Backups

I love using the find command. In DOS, find is like grep. In Linux, find is the most powerful recursive DOS dir /s or Linux ls -r command you could ever put your saddle on.

One of the things you can do with find is to avoid directories, using the -prune switch. Like so:

find /usr/local -type d -a \( -name jre1.6.0_38 -prune -o -type d -print \)

Yeah, put your bike helmet on if you keep reading. That spat out a ton of gook. But was I lying? Well, grep out everything but what we should have pruned:

find /usr/local -type d -a \( -name jre1.6.0_38 -prune -o -type d -print \) | grep jre1.6

What if you have a series of subdirectories you want to include, but you cannot write enough -prune switches for them? This is a problem I frequently have. For instance, how do you exclude all your Firefox Cache directories, especially if you have multiple profiles? Great question.

I’d first use find to find all the directories I

do want to backup:

find /home/jed -maxdepth 4 -type d > /tmp/dirlist

Then you grep out things you really don’t want:

egrep -i "/cache|/Trash" /tmp/dirlist > /tmp/avoid

Then parse it into things you do want to find to avoid:

cat /tmp/avoid | while read F ; do echo " -path $F -o " ; done > /tmp/avoid2 ;
echo "-path ./asdf" >> /tmp/avoid2

Now we can refresh our list of directories to descend:

find . -xdev -depth -type d \( `cat /tmp/avoid2` \) -prune -o -print

If we want to turn that right into files, modify the last print statement to find files:

find . -xdev -depth -type d \( `cat /tmp/avoid2` \) -prune -o -type f -print

Now if you want to find the files more recently created than your last backup in /home/backup/monday.tgz, try this:

find . -xdev -depth -type d \( `cat /tmp/avoid2` \) -prune -o -type f -newer /home/backup/monday.tgz -print

Is that enough to make you cry? Chin up, think of all the disk space you’re saving, and how much faster a specific backup can occur. This means you can run backups every 15 minutes.

Translating Filenames — Bash Voodoo Style #linux

This is an example of using Bash to convert a crazy apache log and translating the filename into an IIS log pattern:

d=`date +%Y%m%d`
find k -type f \
| while read filename
do
  nextfile="${filename/k\/done?0.www\./k2/www-Server_T${d}_}.log"
  echo "$nextfile"
  cat $filename | $translate > "$nextfile"
done

I love how I can refer to a shell variable ($d) inside a string translation (${d}).

Tail the Latest Log File

I’m grateful for Cygwin. I wouldn’t know how to do this in cmd–though I should probably learn how to do it in PoSH.

find LogFiles/W3SVC1 -type f | xargs ls -1tr | tail -n1 | xargs tail -F

I need to make it an alias now….