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 
4
  • 3
    Why not use make? It is explicitly designed to handle this requirement.CommentedAug 4, 2015 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
    CommentedAug 4, 2015 at 16:26
  • That's what wildcards are for.
    – bahamat
    CommentedAug 4, 2015 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).CommentedAug 4, 2015 at 23:10

1 Answer 1

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.

2
  • 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
    CommentedAug 4, 2015 at 16:25
  • I've updated the answer to use some GNU make shortcuts to handle multiple dependencies.CommentedAug 4, 2015 at 17:00

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.