38

How can I run a script, which must execute before all other makefile commands? And it will be nice (but not mandatory) to the script is not executed if there is nothing to build.

I've searched SO and Google, but can't find anything.

I have this workaround:

# myscript.bat output is empty
CHEAT_ARGUMENT = (shell myscript.bat)
CFLAGS += -DCHEAT_ARGUMENT=$(CHEAT_ARGUMENT)
AFLAGS += -DCHEAT_ARGUMENT=$(CHEAT_ARGUMENT)

But it's very ugly. Is there other way to run "pre-build step" in makefile?

pingul
  • 3,351
  • 3
  • 25
  • 43
zxcat
  • 2,054
  • 3
  • 26
  • 40
  • "not execute if nothing to build" is not very important now, but will be good. Question is changed. – zxcat Oct 23 '09 at 10:02
  • What are you ultimately trying to do? What does myscript.bat do? Are you really just trying to compute some extra compiler flags, or is this a contrived example? – Eric Melski Oct 23 '09 at 17:08
  • 1
    My script gets SVN revision and checks, is it modified from last commit. It puts this information to other script (generate rev.bat file), which simple output this information without anything else. Later in makefile I use REVISION = $(shell rev.bat) to get this info. And then: AXF_FILE = project_r$(REVISION)_$(ARCHITECTURE).axf – zxcat Oct 25 '09 at 19:02

5 Answers5

37

I propose two solutions. The first mimics what NetBeans IDE generates:

CC=gcc

.PHONY: all clean

all: post-build

pre-build:
    @echo PRE

post-build: main-build
    @echo POST

main-build: pre-build
    @$(MAKE) --no-print-directory target

target: $(OBJS)
    $(CC) -o $@ $(OBJS)

clean:
    rm -f $(OBJS) target

The second one is inpired by what Eclipse IDE generates:

CC=gcc

.PHONY: all clean
.SECONDARY: main-build

all: pre-build main-build

pre-build:
    @echo PRE

post-build:
    @echo POST

main-build: target

target: $(OBJS)
    $(CC) -o $@ $(OBJS)
    @$(MAKE) --no-print-directory post-build

clean:
    rm -f $(OBJS) target

Note that in the first one, pre and post builds are always called regardless of whether the main build is determined to be up to date or not.

In the second one, the post-build step is not executed if the state of the main build is up to date. While the pre-build step is always executed in both.

Amro
  • 123,847
  • 25
  • 243
  • 454
  • 10
    pre-build doesn't have to run before main-build in the second example as there is no dependancy between them. Watch out for this if you do a parallel make. – Scott Wales Jan 14 '10 at 06:16
  • Scott Wales is correct. I've seen extra dependencies added from pre-build to main-build, and from main-build to post-build, to combat this. – mlepage Sep 10 '13 at 18:34
  • I think you are right... Feel free to suggest/make edits to improve the second example. – Amro Sep 11 '13 at 02:08
  • @mlepage But if you add the extra dependency, won't you now force main-build to run every time make is called, whether or not its dependencies have actually changed? – Jack O'Connor Aug 14 '15 at 20:09
15

Depending on your make version, something like the following should at least avoid running dozens of times if CFLAGS and AFLAGS are evaluated dozens of times:

CHEAT_ARG := $(shell myscript)

Note the colon.

This runs exactly once. Never more than once, but also never less than once. Choose your own tradeoffs.

Update For GNU make 3.80 or above, deferred simple variable expansion might be helpful to execute the script zero times (if it is not needed) or one time (if it is needed one or more times). The implementation involves some non-trivial trickery, which the linked article describes comprehensively. TLDR, adapted for this question's example:

CHEAT_ARG = $(eval CHEAT_ARG := $$(shell myscript))$(CHEAT_ARG)
ndim
  • 35,870
  • 12
  • 47
  • 57
  • Won't that run anyway even if it doesn't need to? The question asks for it not to execute if there is nothing to build. –  Oct 23 '09 at 09:24
  • Oh right. Well, at least it runs exactly once and thus never mover than once, even if there are dozens of make rules expanding CFLAGS and AFLAGS. – ndim Oct 23 '09 at 09:30
  • 1
    ndim, Thank You! This answer is the best way for me. – zxcat Oct 26 '09 at 06:10
  • This solution is broken if the script generates any file that is a dependency of another file: Make doesn't "know" that $(shell) can modify files so it may have cached file timestamps that are from before myscript was run. When this happens you'll get a broken build from out-of-date object files. – olsner Apr 19 '11 at 11:04
  • @olsner: I was under the impression that the shell command in something like "FOO := $(shell foo)" was run by make in a first pass, before make actually starts evaluating the file dependency rules. I am going to check up on that. – ndim Apr 20 '11 at 08:51
  • Is there any way to use this approach, but also have the build fail if `myscript` fails? When I try it it seems to ignore failures. – Jack O'Connor Aug 14 '15 at 20:10
5

You could add a special target to your Makefile and have all your build rules depend on that:

run-script:
    myscript

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

.o.S: run-script
     $(AS) $(AFLAGS) -o $@ $<

Depending on what your script actually does, putting it to run once in a stage before the Makefile (configure stage in autoconf terms) could make even more sense (and be less work).

ndim
  • 35,870
  • 12
  • 47
  • 57
  • It's almost the same, as asveikau suggested. The script will be executed before each file (several times). – zxcat Oct 23 '09 at 09:51
  • And autoconf will result the same as in your first answer, isn't it? – zxcat Oct 23 '09 at 09:53
  • 1
    Uhm, you are right (need more coffee). Only if you can express some proper make deps on your script you can save on invocations. – ndim Oct 23 '09 at 10:15
  • 2
    No the script will be run once at most, no matter how many other targets are made. TRY IT. – Beta Oct 23 '09 at 15:28
3

What you are proposing seems a bit "un-make-like". Why not just run the command in whatever makefile target you need it to go before?

Example, if you need it to run before linking foo:

foo: ${OBJS}
    my-command-goes-here
    ${CC} -o $@ ${OBJS} ${LIBS}
asveikau
  • 39,039
  • 2
  • 53
  • 68
  • 1
    Thanks for answer. Yes, I've tried this too. But I have several $(CC) and $(AS) rules (armasm use different parameters for different architectures - the binary is universal and chooses architecture-dependent code in run-time). And it's not very good idea to add "my-command" in many places. But better then my current method – zxcat Oct 23 '09 at 09:31
  • I can't run it before link, because I need it's results earlier. And I want to run it once, not on every file processing. Before any other file precessing. – zxcat Oct 23 '09 at 09:49
1

Thank you for answers. ndim helped me much, asveikau. The final file is one binary executable, so I can use now something like this:

run-script:
    myscript
$(AXF_FILE): run-script $(OBJ_DIRS) $(OBJ_FILES)
    $(LINK) #......

It will run myscript once. {AXF_FILE} value depends on myscript and I must run it before. And in this case myscript runs always, not only when rebuild is needed. After, The Simplest Answer came to my mind:

all: run-script $(AXF_FILE)

That's all ;) (Of course, any target can be used instead of "all")


Edit: this method execute script after $(AXF_FILE) is calculated too. So it's possible to get wrong value of AXF_FILE. Now only the first answer by ndim works as I need.

zxcat
  • 2,054
  • 3
  • 26
  • 40