0

I'm writing a Makefile for Verilog compilation (isn't important if you aren't familiar with it). The compiler command can either take compile units or a flat file that has the compile units 'in order'. For the latter case, I'd like to write a rule that will spit out a file that has the dependencies in the right order.

Let's assume a simple makefile below:

a.output:
  <a.files>

b.output: a.output
  <b.files>

c.output: d.output
  <c.files>

d.output: d.input
  <d.files>

What I'd like is a file that contains:

a.files
b.files
d.files
c.files

An idea I had was to have a variable in the recipe that can be appended to, like

a.output: 
  MY_FILES += <a.files>

but I don't think that will work in a recipe context. Does anyone have a decent solution to this problem?

Also, I'd like the Makefile to be parallel but obviously it won't work for this target. How can I specify disabling parallel execution for this specific target or set of targets?

EDIT Oct 8 2019

To make it simpler for a person not familiar with Make syntax to write dependencies, I've basically let them write out their intent using variables:

MODULES += module_a
module_a.prerequisites = module_b module c
module_a.other_var = some_string_a

MODULES += module_b
module_b.prerequisites = 
module_b.other_var = some_string_b

I then use a define directive to generate the rules necessary for compilation (I was inspired by this example). This means I do have flexibility on what I can create. So, in the previous example, <a.output> is actually the target for module_a. This can be a PHONY target. <a.files> actually represents the variables (prerequisites and other_var in the example).

I'm sorry for the miscommunication but what I'm trying to do is write out all the target and dependent module_x.other_var in the right order for a given module. I hope that makes it clear.

I'm currently concatenating a file in the right order which is one of the solutions mentioned below. I was wondering if there's some other Make magic that I could apply.

Arun D'souza
  • 182
  • 12
  • Can you clarify mean. On surface, you can not place file name in the make command. Are those input files to the various compilation commands ? – dash-o Oct 08 '19 at 07:50
  • I'm sorry, I think I wrote that pseudo-code very badly. I'll try to edit it for clarity. – Arun D'souza Oct 08 '19 at 18:56

1 Answers1

0

In its simplest form, you can just echo the names to the file

OUTPUT_FILE = output.file

a.output:
    echo "<a.files>" > $(OUTPUT_FILE)

b.output: a.output
    echo "<b.files>" >> $(OUTPUT_FILE)

c.output: d.output
    echo "<c.files>" >> $(OUTPUT_FILE)

d.output: d.input
    echo "<d.files>" >> $(OUTPUT_FILE)

If these files are prerequisites then you can print each dep or target using the auto vars:

Print the target/goal

d.output: d.input
    echo "$@" >> $(OUTPUT_FILE)

print the first prerequisite

d.output: d.input
    echo "$<" >> $(OUTPUT_FILE)

print all prerequisites

d.output: d.input
    echo "$^" >> $(OUTPUT_FILE)

update for parallel build

Not pretty, can certainly improve this, but this will be safe for parallel builds. However as I say I think the echo method alone seems to work ok.

Also note this is not tested, so treat as pseudo code

OUTPUT_FILE = output.file
OUTPUT_FILE_A = output.file.a
OUTPUT_FILE_B = output.file.b
OUTPUT_FILE_C = output.file.c
OUTPUT_FILE_D = output.file.d
OUTPUT_FILES =  $(OUTPUT_FILE_A)
OUTPUT_FILES += $(OUTPUT_FILE_B)
OUTPUT_FILES += $(OUTPUT_FILE_C)
OUTPUT_FILES += $(OUTPUT_FILE_D)

final: a.output b.output c.output d.output
    echo "" > $(OUTPUT_FILE)
    for file in "$(OUTPUT_FILES)" ; do \
        cat $$file >> $(OUTPUT_FILE) ; \
    done

a.output:
    echo "<a.files>" > $(OUTPUT_FILE_A)

b.output: a.output
    echo "<b.files>" > $(OUTPUT_FILE_B)

c.output: d.output
    echo "<c.files>" > $(OUTPUT_FILE_C)

d.output: d.input
    echo "<d.files>" > $(OUTPUT_FILE_D)
code_fodder
  • 15,263
  • 17
  • 90
  • 167
  • I *think* its safe under parallel execution - more down to echo/file writing mechanism then by design. I have certainly used it when I don't really care how the output comes out. But if you want to gaurantee for parallel execution then the safest thing to do is each rule writes to its own file and then a final goal puts all the files together... Ill add a rough example – code_fodder Oct 08 '19 at 10:48
  • This is the solution I came up with which works fine for serial execution. I think parallel execution will clobber the file. The parallel solution you've offered though seems to print out the files in a specific order instead of a dependency order. In this example, it would print `(a,b,c,d)` even though actually `c` depends on `d` – Arun D'souza Oct 08 '19 at 19:03
  • If c depends on d then rule c will not be called until d is created/updated therefore c will be printed last – code_fodder Oct 08 '19 at 19:51
  • Yes, but won't `final` be called after `a`,`b`,`c` and `d`? It looks to me that final simply concatenates the value of `OUTPUT_FILES`, whose order is based on the concatenation in the makefile rather than the dependency. – Arun D'souza Oct 08 '19 at 22:04
  • 1
    @ArunD'souza ah, you actually care about the output order? - as I say the file writes will be in correct order, but the final will just print them in the order specified. Then in that case I would go back to the first example and use somthing like `flock` to lock the file for writing see: https://unix.stackexchange.com/questions/107038/obtain-exclusive-read-write-lock-on-a-file-for-atomic-updates – code_fodder Oct 09 '19 at 05:55