GNU Make how to make a static pattern rule for files that are not in the same directory? - bash

I want to use make and create a static pattern rule that has the target in a output directory, and the prerequisite files are in the preceeding directory, and it has to work recursively.
I have a minimal example here:
.
├── anotherdir
│   ├── output
│   │   ├── source3.md
│   │   └── source4.md
│   ├── source3.json
│   └── source4.json
├── output
│   ├── source1.md
│   └── source2.md
├── source1.json
└── source2.json
I want to generate the output directories if they do not exist, and I want to generate *.md files from the *.json using make if they do not exist, or *.json is updated.
So far, I have the following Makefile:
SOURCE_FILES := $(shell find ./ -name "*.json")
OUTPUT_FILES := $(join $(addsuffix output/,$(dir $(SOURCE_FILES))), $(addsuffix .md,$(basename $(notdir $(SOURCE_FILES)))))
.PHONY: all
all: $(OUTPUT_FILES)
$(OUTPUT_FILES): %.md: %.json
mkdir -p $(dir $#)
# Command to create MD file from json file into the output directory here
The actual command to create the MD file from the json file doesn't matter here, because I have a script that I will call that will do this for me. The problem here, is that when I try to even run this at all, I get the following output:
> make all
make: *** No rule to make target 'anotherdir/output/source4.json', needed by 'anotherdir/output/source4.md'. Stop.
Obviously, source4.json is not in anotherdir/output, but rather, it's in the preceeding directory, which is just anotherdir. I don't know how to make it so that the pattern $(OUTPUT_FILES): %.md: %.json will match it properly.
Or is a static pattern rule not good here? I'm not sure what to do to fit my scenario.
EDIT: I tried to do something like this:
$(OUTPUT_FILES): %.md: $(join $(subst output,,$(dir %)), $(addsuffix .json,$(basename $(notdir %))))
and this doesn't work, I still get:
> make all
make: *** No rule to make target 'anotherdir/output/source4.json', needed by 'anotherdir/output/source4.md'. Stop.
Edit 2: to clarify, i start with the following files
.
├── anotherdir
│ ├── source3.json
│ └── source4.json
├── source1.json
└── source2.json
And then when i run make, i want it to generate the output folders like this
.
├── anotherdir
│ ├── output
│ │ ├── source3.md
│ │ └── source4.md
│ ├── source3.json
│ └── source4.json
├── output
│ ├── source1.md
│ └── source2.md
├── source1.json
└── source2.json
I want to use some kind of smart makefile syntax to pick up these files names without me hard coding it in myself. Hence, i looked at the documentation and saw that static pattern rules might be the solution that i want, except that i can't get the right prerequisite pattern down.

I would do it this way:
First, find the source files just as you did (with a small change to prevent the unsightly double-slash):
SOURCE_FILES := $(shell find . -name "*.json")
A pattern file would be nice, if we could use two wildcards at once, but Make can't quite do that. So I recommend using a template:
define template
TDIR := $(dir $(1))output
TARG := $$(TDIR)/$(notdir $(basename $(1))).md
$$(TARG): $(1)
mkdir -p $$#
#echo building $$# from $$<
# Command to create MD file from json file into the output directory here
endef
$(foreach SOURCE,$(SOURCE_FILES),$(eval $(call template,$(SOURCE))))
If this works, all that's left is to construct a list of output files, and a default rule that has all of them as prerequisites:
define template
TDIR := $(dir $(1))output
TARG := $$(TDIR)/$(notdir $(basename $(1))).md
OUTPUT_FILES += $$(TARG)
$$(TARG): $(1)
mkdir -p $$#
#echo building $$# from $$<
# Command to create MD file from json file into the output directory here
endef
all:
$(foreach SOURCE,$(SOURCE_FILES),$(eval $(call template,$(SOURCE))))
all: $(OUTPUT_FILES)
It isn't pretty, but it seems to work.

If it had not been proposed already in another answer I would have suggested foreach-eval-call. For completeness here are different solutions for GNU make (they may work also with other versions of make but I did not check):
Creating the output directories beforehand
If the output directories exist already you can refer to ../%.json in your pattern rule:
SOURCE_FILES := $(shell find . -name "*.json")
OUTPUT_FILES := $(join $(dir $(SOURCE_FILES)),\
$(patsubst %.json,output/%.md,$(notdir $(SOURCE_FILES))))
$(shell mkdir -p $(dir $(OUTPUT_FILES)))
.PHONY: all
all: $(OUTPUT_FILES)
%.md: ../%.json
: json2md $< -o $#
This may look strange but if you read carefully the Pattern match section of the GNU make manual you should quickly understand. The only constraint for this to work is that the output directories exist before make searches pattern rules that match the targets. If one does not exist make will complain that there is no eligible rule to build the target. This is the reason for the:
$(shell mkdir -p $(dir $(OUTPUT_FILES)))
at the beginning of the Makefile. Demonstration:
$ make
: json2md output/../source2.json -o output/source2.md
: json2md output/../source1.json -o output/source1.md
: json2md anotherdir/output/../source4.json -o anotherdir/output/source4.md
: json2md anotherdir/output/../source3.json -o anotherdir/output/source3.md
Using the secondary expansion
Secondary expansion gives you the possibility to use automatic variables in the list of prerequisites. The $$ are needed to escape the first expansion by make.
SOURCE_FILES := $(shell find . -name "*.json")
OUTPUT_FILES := $(join $(dir $(SOURCE_FILES)),\
$(patsubst %.json,output/%.md,$(notdir $(SOURCE_FILES))))
.PHONY: all
all: $(OUTPUT_FILES)
$(sort $(dir $(OUTPUT_FILES))):
mkdir -p $#
.SECONDEXPANSION:
$(OUTPUT_FILES): $$(patsubst %output,%,$$(#D))$$(basename $$(#F)).json | $$(dir $$#)
: json2md $< -o $#
Demonstration:
$ make
mkdir -p output/
mkdir -p anotherdir/output/
: json2md source2.json -o output/source2.md
: json2md source1.json -o output/source1.md
: json2md anotherdir/source4.json -o anotherdir/output/source4.md
: json2md anotherdir/source3.json -o anotherdir/output/source3.md
Note: instead of creating the output directories in the json-to-md rule (which has the drawback of creating them several times), I added them as order-only prerequisites and added a specific rule to create them.
Note: the sort function also removes duplicates.
Using recursive make
Here we invoke make (with always the same Makefile) recursively in each sub-directory (except output, of course). Each invocation handles only the local json files, which makes the paths of prerequisites and targets much simpler.
MF := $(realpath $(lastword $(MAKEFILE_LIST)))
SUB_DIRS := $(filter-out . ./output,$(shell find . -maxdepth 1 -type d))
SOURCE_FILES := $(filter-out $(SUB_DIRS),$(wildcard *.json))
OUTPUT_FILES := $(patsubst %.json,output/%.md,$(SOURCE_FILES))
.PHONY: $(SUB_DIRS) all
all: $(SUB_DIRS) $(OUTPUT_FILES)
$(OUTPUT_FILES): output/%.md: %.json | output
: json2md $< -o $#
output:
mkdir -p $#
$(SUB_DIRS):
$(MAKE) -C $# -f $(MF)
Demonstration:
$ make
make -C anotherdir -f /home/doe/json2md/Makefile
make[1]: Entering directory '/home/doe/json2md/anotherdir'
mkdir -p output
: json2md source4.json -o output/source4.md
: json2md source3.json -o output/source3.md
make[1]: Leaving directory '/home/doe/json2md/anotherdir'
mkdir -p output
: json2md source2.json -o output/source2.md
: json2md source1.json -o output/source1.md

Related

with Makefile create folders from specific filenames

I need a Makefile that create for every <file.rst> a <file> folder to then execute
hovercraft on the <file.rst> which need a folder as second argument
$ tree
.
├── a.rst
├── b.rst
└── Makefile
With this Makefile
$ cat Makefile
.PHONY: html
HTML_TARGETS:= $(patsubst %.rst,%.html,$(wildcard *.rst))
html: $(HTML_TARGETS)
%.html: %.rst
#rm -fr $(basename $# .html)
#mkdir -p $(basename $# .html)
#hovercraft -Ns $< $(basename $# .html)
$
It kind of works
.
├── a
│   └── index.html
├── a.rst
├── b
│   └── index.html
├── b.rst
└── Makefile
I fell how baroquish this Makefile is, what could be a better way to write it ?
BTW I fail to add in the Makefile this echo:
#echo output done in $(basename $# .html)/index.html
I get:
output done in a /index.html
output done in b /index.html
^
└─ with an unwanted space
I whould like to print:
output done in a/index.html
output done in b/index.html
If I understand correctly that you want to make a directory "x", then execute hovercraft x.rst x/index.html for every file "x.rst", then this should be a succinct way to do so.
SOURCES := $(wildcard *.rst)
TARGETS := $(SOURCES:.rst=/index.html)
%/index.html: %.rst
mkdir -p $*
hovercraft $< $#
.PHONY: all
all: $(TARGETS)

Target is not bind to automatic variable in Makefile job expanded by macro

I'm trying to write jobs that contain a lot of similarities as below.
EMACS_VERS := 22.1 23.4 24.5 25.3 26.1
LOCAL_LISPDIRS := $(patsubst %,local/%/site-lisp,$(EMACS_VERS))
$(addsuffix /leaf, $(LOCAL_LISPDIRS)): site-lisp/leaf
mkdir -p $(#D)
cp -rf site-lisp/$(#F) $#
$(MAKE) --no-print-directory -C $(dir $(#D)) .make-repo-$(#F)
$(addsuffix /orglyth, $(LOCAL_LISPDIRS)): site-lisp/orglyth
mkdir -p $(#D)
cp -rf site-lisp/$(#F) $#
$(MAKE) --no-print-directory -C $(dir $(#D)) .make-repo-$(#F)
$(addsuffix /cort, $(LOCAL_LISPDIRS)): site-lisp/cort
mkdir -p $(#D)
cp -rf site-lisp/$(#F) $#
$(MAKE) --no-print-directory -C $(dir $(#D)) .make-repo-$(#F)
However, when those job was expanded by below macro, the target name was not bind to the automatic variable, and an error occurred.
define build_repo
$1: $2
mkdir -p $(#D)
cp -rf site-lisp/$(#F) $#
$(MAKE) --no-print-directory -C $(dir $(#D)) .make-repo-$(#F)
endef
$(eval $(call build_repo,$(addsuffix /leaf,$(LOCAL_LISPDIRS)),site-lisp/leaf))
$(eval $(call build_repo,$(addsuffix /orglyth,$(LOCAL_LISPDIRS)),site-lisp/orglyth))
$(eval $(call build_repo,$(addsuffix /cort,$(LOCAL_LISPDIRS)),site-lisp/cort))
The above code gets the following error. As I think, this happens because the name of the target is not an automatic variable bound.
mkdir -p
usage: mkdir [-pv] [-m mode] directory ...
make: *** [Makefile:72: local/22.1/site-lisp/leaf.el] Error 64
Is there a way to solve this problem?
On the other hand, the following code has been rejected as changes to leaf, orglyth, cort are all generated when change files in only leaf.
REPOS := leaf orglyth cort
REPODIRS := $(addprefix site-lisp/, $(REPOS))
LOCAL_REPOS := $(foreach repo, $(REPOS), $(addsuffix /$(repo), $(LOCAL_LISPDIRS)))
$(LOCAL_REPOS): $(REPODIRS)
mkdir -p $(#D)
cp -rf site-lisp/$(#F) $#
$(MAKE) --no-print-directory -C $(dir $(#D)) .make-repo-$(#F)
Directory tree:
local
├── 22.1
│   └── site-lisp
│   ├── cort
│   ├── leaf
│   └── orglyth
├── 23.4
│   └── site-lisp
│   ├── cort
│   ├── leaf
│   └── orglyth
├── 24.5
│   └── site-lisp
│   ├── cort
│   ├── leaf
│   └── orglyth
├── 25.3
│   └── site-lisp
│   ├── cort
│   ├── leaf
│   └── orglyth
└── 26.1
└── site-lisp
├── cort
├── leaf
└── orglyth
site-lisp
├── cort
├── leaf
└── orglyth
Your macro is expanded twice. Double all $ signs to escape the first expansion.
As you have two nested loops (versions and repos) it would be difficult to use pattern rules instead of macros. But you can probably use macros a bit more efficiently:
# $(1): repo
# $(2): version
define build_repo
local/$(2)/site-lisp/$(1): site-lisp/$(1)
mkdir -p $$(#D)
cp -rf site-lisp/$$(#F) $$#
$$(MAKE) --no-print-directory -C $$(dir $$(#D)) .make-repo-$$(#F)
endef
$(foreach r,$(REPOS),$(foreach v,$(EMACS_VERS),$(eval $(call build_repo,$(r),$(v)))))
Or:
# $(1): repo
# $(2): version
define build_repo
local/$(2)/site-lisp/$(1): site-lisp/$(1)
mkdir -p local/$(2)/site-lisp
cp -rf site-lisp/$(1) local/$(2)/site-lisp/$(1)
$(MAKE) --no-print-directory -C local/$(2) .make-repo-$(1)
endef
$(foreach r,$(REPOS),$(foreach v,$(EMACS_VERS),$(eval $(call build_repo,$(r),$(v)))))
The second version does not need any $ escape because everything is correctly and completely expanded at the first expansion. Yes, even $(MAKE) that, most likely in your case, expands the same during the first or the second expansion.
But remember that this is a special case. If you continue using the $(eval...) function do not forget the double expansion...
Thanks #Renaud Pacelet for your good reply!
define build_repo
$1: $2;
mkdir -p $$(#D)
cp -rf site-lisp/$$(#F) $$#
$$(MAKE) --no-print-directory -C $$(dir $$(#D)) .make-repo-$$(#F)
endef
$(call build_repo,$(addsuffix /leaf,$(LOCAL_LISPDIRS)),site-lisp/leaf)
$(call build_repo,$(addsuffix /orglyth,$(LOCAL_LISPDIRS)),site-lisp/orglyth)
$(call build_repo,$(addsuffix /cort,$(LOCAL_LISPDIRS)),site-lisp/cort)
With this code I got what I wanted without error. But is there a smarter another way to not use macros?
[Additional notes]
I looked at the answer of #Renaud Pacalet and reconcidered.
Actually, when repositories are changed, I have to build on all versions, so I can do one loop as follows.
LOCALDIRS := $(addprefix local/, $(EMACS_VERS))
define build_repo
$(addsuffix /site-lisp/$(1), $(LOCALDIRS)): $(LISPDIR)/$(1)
mkdir -p $$(#D)
cp -rf site-lisp/$$(#F) $$#
$$(MAKE) --no-print-directory -C $$(dir $$(#D)) .make-repo-$$(#F)
endef
$(foreach repo, $(REPOS), $(eval $(call build_repo,$(repo))))

Collect files from another folder

I developed a script to convert all the markdowns files in my folder to ePub and place them in epubs.
Nevertheless I would like to place my files in the markdowns sub-folder and not in the main directory (the one where the makefile is).
.
├── epubs
│   └── blabla.epubs
├── makefile
└── markdowns
└── blabla.pd
How should I modify my code?
MARKDOWN=$(shell find . -iname "*.pd")
EPUBS=$(MARKDOWN:.pd=.epub)
.PHONY = all clean
all: $(EPUBS)
%.epub: %.pd
pandoc --from markdown --to epub --smart $< -o epubs/$#
clean:
rm epubs/*
One approach is to explicitly tell make in your pattern rule where the .pd prerequisites for the .epub targets reside:
MARKDOWN_DIR := markdowns
EPUB_DIR := epubs
MARKDOWNS := $(wildcard $(MARKDOWN_DIR)/*.pd)
EPUBS := $(subst $(MARKDOWN_DIR),$(EPUB_DIR),$(MARKDOWNS:.pd=.epub))
$(EPUB_DIR)/%.epub: $(MARKDOWN_DIR)/%.pd
pandoc --from markdown --to epub --smart $< -o $#
.PHONY: all clean
all: $(EPUBS)
Note two minor additional changes. I replaced your shell find command with make's $(wildcard) function because that is what it's for. Your pandoc command no longer needs epubs/ in front of $# because that directory is now part of the target. And I reordered a bit according to my liking.

Makefile: explicit entry works, wildcard % not working

I have this rule in my makefile, but make didn't find it:
$(BUILDDIR)%.o : $(BUILDDIR)%.bin
#echo
$(OBJCOPY) -I binary -O elf32-avr --redefine-sym _binary_$*_bin_start=$* --redefine-sym _binary_$*_bin_end=$*_end $< $#
If I make it explicit, make will use them (called for build/rom1.o and build/rom2.o; BUILDDIR=build/)
$(BUILDDIR)rom1.o : $(BUILDDIR)rom1.bin
#echo
$(OBJCOPY) -I binary -O elf32-avr --redefine-sym _binary_rom1_bin_start=rom1 --redefine-sym _binary_rom1_bin_end=rom1_end $< $#
$(BUILDDIR)rom2.o : $(BUILDDIR)rom2.bin
#echo
$(OBJCOPY) -I binary -O elf32-avr --redefine-sym _binary_rom2_bin_start=rom2 --redefine-sym _binary_rom2_bin_end=rom2_end $< $#
Does anyone has a hint whats wrong with the wildcard in my first try?
Edit:
The version of make is 4.1 running on Ubuntu 16.04.
This is the error message from make when trying to run with the wildcard:
make: *** No rule to make target 'build/rom1.o', needed by 'build/rom1.elf'. Stop.
Let's simplify the problem…
If this is your file structure…
.
├── Makefile
└── build
├── rom1.bin
└── rom2.bin
and this is your Makefile…
BUILDDIR := build/
$(BUILDDIR)%.o: $(BUILDDIR)%.bin
touch $#
and you run $ make build/rom{1,2}.o, your resulting file structure will be…
.
├── Makefile
└── build
├── rom1.bin
├── rom1.o
├── rom2.bin
└── rom2.o

Using GNU Make with subdirectories

I was wondering what different approaches of using Make in a project with subdirectories exist, and what are their advantages/drawbacks, but could never see a good summary or cookbook.
I have seen in my researches mainly the "recursive" and "single makefile" approaches, but are there others ?
I also assume that there is not only one "recursive" or "single makefile" approaches but several, so could somebody sum it up ?
For my particular case, I would like a directory architecture looking like this:
.
├── build
│   ├── *.d
│   ├── *.o
| ├── subdir1
| │ ├── *.d
| │ └── *.o
| └── subdir2
| ├── *.d
| ├── *.o
| └── subdir3
| ├── *.d
| └── *.o
├── include
│   ├── *.h
│   └── *.h
├── Makefile
└── src
├── *.c
├── *.h
├── subdir1
│   ├── *.c
│   └── *.h
└── subdir2
├── *.c
├── *.h
└── subdir3
├── *.c
└── *.h
Which solution should I choose ? Possibly one which would allow source files with the same name ?
Your project setup is really basic, so should be your Makefile:
SRC_DIR := src
BLD_DIR := build
SRC := $(shell find $(SRC_DIR) -name "*.c")
OBJ := $(SRC:$(SRC_DIR)/%.c=$(BLD_DIR)/%.o)
DEP := $(OBJ:.o=.d)
CPPFLAGS := -MMD -MP # enable auto-dependency generation
CFLAGS := -Wall -W -pedantic
.PHONY: all clean
all: $(OBJ)
clean:
$(RM) -r $(BLD_DIR)
.SECONDEXPANSION:
$(BLD_DIR)/%.o: $(SRC_DIR)/%.c | $$(#D)/ # First check that the destination directory exists
$(CC) $(CPPFLAGS) $(CFLAGS) -o $# -c $<
%/:
mkdir -p $* # -p flag necessary for recursive directory creation
ifeq "$(MAKECMDGOALS)" ""
-include $(DEP)
endif
The idea here is to list source files recursively using the find command, to supply make with the appropriate pattern rule to compile in the right place and pass the right preprocessor file to your compiler to enable auto-dependency generation.
Tested with GNU Make 4.1 under Windows 8.1 with the GIT Bash shell and the following directory structure:
.
├── Makefile
└── src
├── test.c
├── test1.c
└── subdir1
└── test.c
After reading Recursive Make Considered Harmful, I figured a quite simple and modular way to achieve this, by having files in all subdirectories that would include each other and be included in the main makefile:
CXX := gcc
SRCDIR := src
OBJDIR := build
# These lines are needed to set immediate evaluation for
# these variables, instead of deferred evaluation which is unsuitable.
SRCS :=
SUBDIRS :=
CFLAGS :=
LDFLAGS :=
include $(SRCDIR)/module.mk
OBJS := $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))
SRCS := $(addprefix $(SRCDIR)/, $(SRCS))
DEPS := $(OBJS:.o=.d)
TMPS := $(OBJS) $(OBJS:.o=.d)
CFLAGS += -MD
debug: CFLAGS += -g -g3 -ggdb
CFLAGS += $(addprefix -I./$(SRCDIR)/, $(SUBDIRS))
LDFLAGS += -lsomelib
debug: LDFLAGS += -g -g3 -ggdb
NAME := yolo
all: $(NAME)
debug: re
-include $(DEPS)
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(NAME): $(OBJS)
#$(CXX) $(OBJS) -o $(NAME) $(LDFLAGS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
#mkdir -p $(OBJDIR)
#for dir in $(SUBDIRS); \
do \
mkdir -p $(OBJDIR)/$$dir; \
done
clean:
rm -rf $(TMPS)
fclean: clean
rm -rf $(NAME)
rm -rf $(OBJDIR)
re: fclean all
.PHONY: all clean fclean re
And in every subdirectory, a module.mk file (I could have named it anything, but this seemde cool).
For src:
SRCS := main.c file1.c file2.c
SUBDIRS += subdir1 subdir2
include $(SRCDIR)/subdir1/module.mk
include $(SRCDIR)/subdir2/module.mk
For a level 1 subdirectory:
THIS_DIR_L0 := subdir1
MOD_SRC := file3.c file4.c
SRCS += $(addprefix $(THIS_DIR_L0)/, $(MOD_SRC))
SUBDIRS += $(THIS_DIR_L0)/subdir3
include $(SRCDIR)/$(THIS_DIR_L0)/subdir3/module.mk
And for a level 2 subdir (one deeper):
THIS_DIR_L1 := subdir3
MOD_SRC := file5.c file6.c
SRCS += $(addprefix $(THIS_DIR_L0)/$(THIS_DIR_L1)/, $(MOD_SRC))
And so on...
This is quite simple to set up, I find it very modular and it does not use recursive makefiles. It would not be complicated to make librairies and stuff inside your directory structure either.
Anybody having a better idea please tell me.

Resources