Search code examples
makefilegnu-makeorder-of-execution

Ignore order-only prerequisites


I have set of targets, that should be executed according to some order requirements. There is correct order:

  1. wake
  2. fuse
  3. flash
  4. eep
  5. verify
  6. lock

Some of those targets are grouped as another (phony) empty targets:

.PHONY: wake
full: fuse flash eep lock

Unfortunately, when i call make full --dry-run i see that make tries to execute rules in incorrect order: wake flash eep lock fuse. Therefore i get incorrect results (erased-out MCU).

I've tried to add some order-only prerequisites. Here is skeleton of my Makefile with those:

.PHONY: wake
wake:
        <...>

.PHONY: flash
flash: build/main.hex | fuse
        <...>

.PHONY: eep
eep: build/main.eep.hex | fuse flash
        <...>

.PHONY: verify
verify: build/main.hex | fuse flash eep
        <...>

.PHONY: fuse
fuse: | wake
        <...>

.PHONY: lock
lock: | fuse flash eep
        <...>

build/main.hex: <some files here>
        <...>

Now make full works fine, but PHONY order-only target's rules are executed even when i try to execute some specific rule. For example: make eep results in execution of wake fuse flash eep. I don't want to waste time for full flashing while i need only to write new eep image.

Seems like so-called "order-only prerequisites" are treated like regular prerequisites.

How can i force "order-only prerequisites" to work like real order-only requirements? Is there more elegant solution?


Solution

  • I think you misunderstand what order-only refers to. Order only means that a prerequisite must be built before the target, but if the order-only prerequisite is newer than the target, it does not require the target to be rebuilt. But, if the order-only prerequisite does not exist, the target is still considered out of date (and because all of your targets are .PHONY, none of these exist...)

    Instead of using phony targets, you can use 'sentinel' files, which are basically dummy files that mark if/when each of the targets was last run:

    .PHONY: flash fuse wake eep verify lock all
    
    all:   verify
    flash: .flash.sentinel
    fuse:  .fuse.sentinel
    wake:  .wake.sentinel
    eep:   .eep.sentinel
    
    
    .flash.sentinel: build/main.hex .fuse.sentinel
        <...>
        touch $@
    
    .fuse.sentinel: .wake.sentinel
        <...>
        touch $@
    
    .eep.sentinel: build/main.eep.hex fuse.sentinel flash.sentinel
        <...>
        touch $@
     
    .wake.sentinel: build/main.eep.hex fuse.sentinel flash.sentinel
        <...>
        touch $@
    
    verify: build/main.hex eep.sentinel fuse.sentinel flash.sentinel
        <...>
    

    Notice, that on a clean build, make all will rebuild everything generating the .sentinel files. Subsequent calls to make all will only rebuild anything that is considered out of date. So for example, if you modified build/main.hex, it would rebuild flash, eep, wake and fuse, but if you don't change anything, it would not do anything (because everything is already done). If you build make flash, it will rebuild flash, and any other prerequisites it considers out of date.

    Now, if, on the other hand you wanted to force only the flash rule to run when you type make flash, regardless of whether any of the other targets are out of date, then you could add a symmetrical tree, one that has prereq's and one that doesn't:

    .PHONY: flash fuse wake eep verify lock 
    .PHONY: do_flash do_fuse do_wake do_eep do_verify do_lock
    .PHONY: all
    
    all:   do_lock
    
    flash do_flash:
       <...>
    
    fuse do_fuse:
       <...>
    
    wake do_wake:
       <...>
    
    eep do_eep:   
       <...>
    
    verify do_verify:
       <...>
    
    lock do_lock:
       <...>
    
    do_fuse : do_wake 
    do_flash : do_fuse 
    do_eep : do_flash 
    do_verify : do_eep 
    do_lock : do_verify 
    

    Which means if you do make flash, it has no prerequisites, and will only run the rule. If you do make all, all has prerequisite do_verify, which has another prerequisite, etc, all the way down, and all of these would be run in the correct order.