I want to construct my linker flags LDFLAGS from a list of libraries with their full paths. Here's where/why I'm stuck. (The library names are Linux style, but that shouldn't really matter for how to solve this, I think.)
FULL_LIBS = \
./path1/libone.a \
../../path2/libtwo.a
# etc.
LIB_DIRS = $(dir $(FULL_LIBS))
LIB_NAMES = $(basename $(notdir $(FULL_LIBS)))
LIB_NAMES_TRIMMED = # zzz trim leading "lib" somehow??? This is what I don't know how to do, or at least the best way
LDFLAGS += \
$(addprefix -L, $(LIB_DIRS)) \
$(addprefix -l, $(LIB_NAMES_TRIMMED))
The desired result, if it's not obvious, should be like
LDFLAGS += -L./path1 -L../../path2 -lone -ltwo
Not sure if I NEED regular expressions (or sed/awk/grep), so I'll withhold that tag (regex) for now.
You can use the text processing capabilities of make (https://www.gnu.org/software/make/manual/html_node/Text-Functions.html#Text-Functions) :
LIB_NAMES_TRIMMED = $(subst lib,,$(LIB_NAMES))
Although this could fail if the string "lib" appears elsewhere in the library names. To avoid that we can use 2 steps:
LIB_NAMES_TRIMMED = $(basename $(notdir $(subst /lib,/,$(FULL_LIBS))))
As explained by Matt, it's even simpler with patsubst:
LIB_NAMES_TRIMMED = $(patsubst lib%,%,$(LIB_NAMES))
Related
I have a makefile directive defined as follows:
OBJ = $(SRC:%.c=$(BUILDDIR)/%.o)
where $SRC is defined as
file1.c file2.c etc..
This works just fine when all files are at the same level as the makefile.
If they are in a directory i.e ./src/file1.c ./src/file2.c it does not.
It fails because it tries to append the source path to the build path i.e
gcc -o ./build/./src/file1.o src/file.c
.
I tried to use the nodir keyword but no luck.
I'm looking for the build command to look like this
gcc -o ./build/file1.o ./src/file.c
Any help is much appreciated
Let's try an experiment:
SRC = ./src/file1.c ./src/file2.c
OBJ = $(notdir $(SRC):%.c=$(BUILDDIR)/%.o)
apps: $(OBJ)
We get:
make: *** No rule to make target `file1.c', needed by `apps'. Stop.
Something's amiss here. Let's look at OBJ:
$(info $(OBJ))
We get:
file1.c %.o
Yikes! We constructed OBJ incorrectly. What if we break it up into smaller steps?
SIMPLE_SRC = $(notdir $(SRC))
OBJ = $(SIMPLE_SRC:%.c=$(BUILDDIR)/%.o)
$(info $(OBJ))
We get:
./build/file1.o ./build/file2.o
It turns out that substitution references of the form $(var:%a=%b) do not allow us to put functions in the var. At this point we should probably forgo them and write the full pattern substitution:
OBJ = $(patsubst %.c,$(BUILDDIR)/%.o,$(notdir $(SRC)))
$(info $(OBJ))
We get:
./build/file1.o ./build/file2.o
I'm using GNU make, which I thought would make this easier.
I'm trying to build in either build/Debug or build/Release, depending on whether the target is 'debug' or 'release'.
Here's the relevant excerpt of my makefile:
# Project files
SRC_DIRS = ../source ..source/[...], etc.
SRCS := $(shell find $(SRC_DIRS) -maxdepth 1 -name *.cpp -or -name *.cxx -or -name *.c -or -name *.s)
SRCS := $(sort $(SRCS))
vpath %.cpp $(SRC_DIRS)
vpath %.cxx $(SRC_DIRS)
vpath %.c $(SRC_DIRS)
# Object files
OBJS := $(addsuffix .o, $(notdir $(basename $(SRCS))))
# Dependencies
DEPS := $(OBJS:.o=.d)
debug: DEPS_DIR = build/Debug
release: DEPS_DIR = build/Release
DEPS := $(addprefix $(DEPS_DIR)/, $(DEPS))
include $(DEPS)
The problem is that I can't get $(DEPS) to evaluate properly. It seems like the DEPS addprefix is being evaluated before the target-specific DEPS_DIR assignments, so that it's resulting in "/filename.d", since it thinks $DEPS_DIR is blank. However, if I go down further and under the actual targets add a echo "DEPS_DIR = $(DEPS_DIR)", it expands correctly.
There's probably something obvious I'm doing wrong. How can I point include at the DEPS list in either of build/Debug or build/Release, depending on target?
You've missed the part of the documentation on target-specific variables where it specifies that they are in effect only when expanding the recipes for rules.
Make works by reading in the entire makefile all at once and building its internal graph of targets and prerequisites, and only after that is complete does it look to see which targets should be built based on the command line, etc. That's why order of rule definition, etc. mostly doesn't matter: make is not "running" the makefile as it is read in, as if it were a script. So there's no way for a target-specific variable to impact what files are included by the include statement.
There are a few ways to do what you want. The simplest and most portable one is to use a recursive invocation of make, like this:
debug:
$(MAKE) DEPS_DIR=build/Debug realtarget
release:
$(MAKE) DEPS_DIR=build/Release realtarget
Alternatively the MAKECMDGOALS make variable contains whatever goals were specified on the command line, so you can check this in a make ifeq statement or similar and set DEPS_DIR that way.
I have a bunch of lines like this in my makefile, in different permutations. I want to automate them with a general rule, so if I type $ make foo-WHATEVER, make knows how to build it from foo.c and relevant -D flags.
foo-PARAMA.o: foo.c
foo-PARAMA.o: CPPFLAGS += -DPARAMA
foo-PARAMA-PARAMB.o: foo.c
foo-PARAMA-PARAMB.o: CPPFLAGS += -DPARAMA -DPARAMB
foo-PARAMA-PARAMB-PARAMC.o: foo.c
foo-PARAMA-PARAMB-PARAMC.o: CPPFLAGS += -DPARAMA -DPARAMB -DPARAMC
Well, you can try something like this:
foo-%.o : foo.c
$(CC) $(CPPFLAGS) $(addprefix -D,$(subst -, ,$*)) $(CFLAGS) -o $# -c $<
but my suspicion is that you're really going to want to do this for "any source file" not just foo.c. That's much harder because you can't have multiple patterns in a single target or prerequisite.
For that you'll have to know the list of source files up-front and use eval:
SRCS = foo.c bar.c baz.c biz.c
define make-pattern
$1-%.o : $1.c
$$(CC) $$(CPPFLAGS) $$(addprefix -D,$$(subst -, ,$$*)) $$(CFLAGS) -o $$# -c $$<
endif
$(foreach S,$(SRCS),$(eval $(call make-pattern,$S)))
It looks like you want to do configuration management inside make. The problem is that make itself is not really equipped with such functionality. My guess is that at the time of its invention the extreme diversity of platforms which one day will run some unixoid build system simply wasn't foreseeable - so the main use case was to easily define a rather homogenous build with some small deviations.
Now, its 2019 and make is still around and supposedly with a larger user base than ever before. One possible remedy which has the advantage that it doesn't require any other tool is to use gmtt which is a GNUmake library for exactly this configuration purpose. Below I sketch a hypothetical config scenario:
include gmtt/gmtt.mk
PLATFORM := $(shell uname)
# table of platforms with 2 columns. Don't forget that the leading element of every table must be an integer denoting the number of columns!
define AVAILABLE-PLATFORMS :=
2
CYGWIN?NT-4.? toolX
CYGWIN?NT-10.? toolY
Linux* toolZ
FreeBSD toolXYZ
endef
define ADDITIONAL-FLAGS :=
2
toolX -Dfoo
toolY -Dbar
toolZ -Dbaz
toolXYZ -Dfoo
toolXYZ -Dbar
toolXYZ -Dbaz
endef
# select column 2 from the table line(s) which glob-match the current platform:
USED-TOOL := $(call select,2,$(AVAILABLE-PLATFORMS),$$(call glob-match,$(PLATFORM),$$1))
# now select the right flags for the tool
CFLAGS_OPT := $(call select,2,$(ADDITIONAL-FLAGS),$$(call str-eq,$(USED-TOOL),$$1))
$(info $(USED-TOOL) Options: $(CFLAGS_OPT))
Admittedly, this is a lemma on Greenspun's tenth rule but the other options (composing configuration information with a handful of external tools) aren't so attractive either.
I'd like to generate two output words for each input word, that they be consecutive, and apply patsubst to them.
A non-working prototype of what I want:
$(patsubst %.o,%.a.o %.so.o,$(OBJ))
The second apearance of % is not substituted, and instead a plain % appears.
A working version that does not give the desired order:
$(patsubst %.o,%.a.o,$(OBJ)) $(patsubst %.o,%.so.o,$(OBJ))
Example:
Input:
foo.o bar.o
Desired output:
foo.a.o foo.so.o bar.a.o bar.so.o
How can I get the desired output?
The join make function takes two word lists and concatenate them word by word. Not exactly what you want because there would be no space between foo.a.o and foo.so.o, but not far from it.
Assuming you know for sure that some string will never be part of your file names (e.g. !!!) you can combine substitution references (or the equivalent patsubst), join and subst:
$(subst !!!, ,$(join $(OBJ:o=a.o!!!),$(OBJ:o=so.o)))
Or, even simpler, if you know for sure that .a.o cannot be part of your files' basename:
$(subst .a.o,.a.o ,$(join $(OBJ:o=a.o),$(OBJ:o=so.o)))
Two nested foreach loop could be solve your problem. One for the files and the other one for extensions. The below code should solve your problem.
OBJS = foo.o bar.o
EXTS = a.o so.o
OUT = $(foreach file, ${OBJS}, \
$(foreach ext, ${EXTS}, \
$(patsubst %.o, %.${ext}, ${file})))
I have a directory structure like
packages/
foo/
lib/
a.js
b.js
bar/
lib/
c.js
d.js
and I'm trying to write a simple Makefile to run the source files through a compiler and output the result of compiling packages/foo/lib/a.js to packages/foo/build/a.js. In the past, I've done something like
JS = $(shell find packages/foo/lib -name "*.js")
BUILD = $(patsubst packages/foo/lib/%.js, packages/foo/build/%.js, $(JS))
.PHONY: all
all: $(BUILD)
packages/foo/lib/%.js: packages/foo/build/%.js
# Compile $^ and output to $#
which works great. However, I'm now doing
JS = $(shell find packages/*/lib -name "*.js")
BUILD = $(patsubst ...)
The problem here is that patsubst doesn't seem to like multiple % wildcards (ie packages/%/lib/%.js). However, if I use packages/%.js, then I can't know which directory to use in the substitution. I'm convinced there's a very simple way to do this that I can't find in the make docs.
To define BUILD, you could use the stupid but simple solution:
BUILD = $(subst /lib/,/build/,$(JS))
as long as you aren't worried about additional "lib" and "build" pathname components in your source.
However, this leaves you with the problem of how to define the actual Makefile rules, unless you want to manually specify a rule for each directory foo, bar, etc.
Instead, maybe try something like this (remember to replace spaces with tabs again where appropriate, if you cut and paste):
PACKAGES = $(wildcard packages/*)
ALL :=
define add_package
JS := $$(shell find $(1)/lib -name "*.js")
BUILD := $$(patsubst $(1)/lib/%.js, $(1)/build/%.js, $$(JS))
$(1)/build/%.js:: $(1)/lib/%.js
echo COMPILE $$^ '->' $$#
cp $$^ $$# # super fast compiler!
ALL += $$(BUILD)
endef
$(foreach p, $(PACKAGES), \
$(eval $(call add_package,$(p))))
all: $(ALL)
This evaluates a template for each package directory, so you can put anything in there, as long as you get the fiddly doubling of dollar signs correct.