3

I am trying to do is create a bash script that will run another bash script to compile some programs only if the source has changed. What I have so far is a way to get the timestamps for each file in seconds since J2000 epoch:

#get a list of the source timestamps
sourceTimes=$(stat -f "%Sm" -t "%s" *.f)

#get a list of the program timestamps
exeTimes=$(stat -f "%Sm" -t "%s")

and an install script

./make_common_lib.bsh build
./make_common_lib.bsh install

I want to figure out how to create an if state such that (in semi-pseudocode)

if [any of $sourceTimes>$exeTimes];
then  #run make_common_lib.bsh script
else  #Do nothing
fi

My best guess so far is to somehow use read but I am not sure how I could index both variables at the same time.

If there is a really easy way to do this I apologize as I am very new to bash scripting, but so far none of my google searches have really returned anything useful.

Some more information:

The timestamp variables are in a format like

1432326068 1432326069 1432326069 1432326069 1432326069 1432326069 1432326069 1432326069 1432326069 1432326069 1432326069 1432326069 1432326068 1432326069 1432326069
  • 3
    Why not use make? It is explicitly designed to handle this requirement. – Stephen Kitt Aug 4 '15 at 16:11
  • I do realize this but I'm actually working with someone else's program and for some reason they didn't use make. The problem with make (I think) is that I would need to write out each dependency and there are more than 100 in the common libraries alone (and there's an entire other group of files that I would have to do the same thing for). – Andrew Aug 4 '15 at 16:26
  • That's what wildcards are for. – bahamat Aug 4 '15 at 19:58
  • @Andrew What people usually do is to automatically generate the list of dependencies (usually you run make depend or make dep to regenerate the list). – Gilles Aug 4 '15 at 23:10
3

Your Build Script Method

If you want to keep to the "bash build method", then you are probably best off "touching" a file (touch lastbuild) when the build script is ran and completes the build. In addition the build script could then look for the file generated by touch (if it doesn't exist, assume a build is needed), or if it exists use find to see if any newer files exist:

find . -name "*.[ch]" -newer lastbuild

and then build if that output is 1 or more lines (could be checked with something like wc -l).

Using Make instead

This is best managed by something like a Makefile (specifically used to do this kind of dependency checking).

default: all

all: dependency1.o dependency2.o

dependency1.o: dependency1.c
    ./make_common_lib.bsh build

dependency2.o: dependency2.c
    ./make_common_lib.bsh build

install: 
    ./make_common_lib.bsh install

Creating a dummy "build" script:

$ cat make_common_lib.bsh 
#! /bin/sh

echo "Build $1"

We can now run make:

$ make
./make_common_lib.bsh build
Build build
./make_common_lib.bsh build
Build build

You could also replace the ./make_common_lib.bsh build with the command that ./make_common_lib.bsh build would issue to build dependency1.o etc:

dependency1.o: dependency1.c
    gcc -c dependency1.c

Makefiles also allow for symbol substitution, so you could declare the complier and compiler flags earlier in the Makefile:

CC=/usr/bin/gcc
CFLAGS=-O2 -Wall

and then make references to them in your rules:

dependency1.o: dependency1.c
    $(CC) $(CFLAGS) -c dependency1.c

Note that the line that is indented after a dependency declaration must start with a tab and not spaces.

Shortening dependency rules list

The OP asked if it's possible to do shorter ways of declaring all dependencies. It is possible with a few tricks using GNU's make (note not all these will work with vanilla make).

You can do variable substitution. Given the declaration:

SRCS=dependency1.c dependency2.c dependency3.c

You can then create an objects rule using variable substitution:

OBJS=$(SRCS:.c=.o)

this will replace all .c's with .o's. In effect giving a line of the form:

OBJS=dependency1.o dependency2.o dependency3.o

You can then further do a shortening of the "compile command" using the special variables $< and $@:

.c.o:
    $(CC) $(CFLAGS) -c $<  -o $@

$< represents the prerequisite in GNU make parlance (or dependency as I've called it) and $@ the target, and so it will end up issuing:

/usr/bin/gcc -Wall -O2 -c dependency1.c  -o dependency1.o
/usr/bin/gcc -Wall -O2 -c dependency2.c  -o dependency2.o
.
.
.

Putting this all together, with linking options and a command to link and compile the executable $(TARGET):

# Globals
CC=/usr/bin/gcc
CFLAGS=-Wall -O2
LDFLAGS=-L/usr/local/lib
LIBS=-ldependencylib

# declare all the sources
SRCS=dependency1.c dependency2.c

# declare the objects files using variable substitution (find .c and replace with .o)
OBJS=$(SRCS:.c=.o)

# Target executable name:
TARGET=myexefile

default: all

all: $(TARGET)
    @echo Target has been built

$(TARGET): $(OBJS) 
    $(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LIBS)

.c.o:
    $(CC) $(CFLAGS) -c $<  -o $@

install:
    ./make_common_lib.bsh install

Note that there are many things you can do with GNU make, and it is well documented here GNU Make Manual.

  • thanks for the response. I wish that the people who originally designed this software had done a straight up make file but they haven't. One question I have is, is there someway to do this without having to write out each dependency? There are over 100 separate files that need to be checked and I don't really want to have to write out a dependency for each one. – Andrew Aug 4 '15 at 16:25
  • I've updated the answer to use some GNU make shortcuts to handle multiple dependencies. – Drav Sloan Aug 4 '15 at 17:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.