GNU-make: remove first directories - makefile

I'd like to simplify the following GNU make rules:
lib/dir1/org/eclipse/jetty/jetty-http/9.3.0.M2/jetty-http-9.3.0.M2.jar:
mkdir -p $(dir $#) && curl -o $# "http://central.maven.org/maven2/org/eclipse/jetty/jetty-http/9.3.0.M2/jetty-http-9.3.0.M2.jar"
lib/dir1/org/eclipse/jetty/jetty-util/9.3.0.M2/jetty-util-9.3.0.M2.jar:
mkdir -p $(dir $#) && curl -o $# "http://central.maven.org/maven2/org/eclipse/jetty/jetty-util/9.3.0.M2/jetty-util-9.3.0.M2.jar"
lib/dir1/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar:
mkdir -p $(dir $#) && curl -o $# "http://central.maven.org/maven2/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar"
lib/dir1/org/slf4j/slf4j-api/1.7.10/slf4j-api-1.7.10.jar:
mkdir -p $(dir $#) && curl -o $# "http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.10/slf4j-api-1.7.10.jar"
how can I remove the prefix 'lib/dir1' in the curl URL to get a simple rule like:
$(addsuffix lib/dir1/,org/eclipse/jetty/jetty-http/9.3.0.M2/jetty-http-9.3.0.M2.jar org/eclipse/jetty/jetty-util/9.3.0.M2/jetty-util-9.3.0.M2.jar javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar org/slf4j/slf4j-api/1.7.10/slf4j-api-1.7.10.jar):
mkdir -p $(dir $#) && curl -o $# \
"http://central.maven.org/maven2/$(something $#)"
something like 'substring-after'...

Doesn't $(patsubst lib/dir1/%,%,$#) work?

Related

makefile error: No rule to make target How to fix?

The error:
make: *** No rule to make target 'build-x86_64'. Stop.
My makefile:
x86_64_asm_source_files := $(shell find src/impl/x86_64 -name *.asm)
x86_64_asm_object_files := $(patsubst src/impl/x86_64/%.asm, build/x86_64/%.o, $(x86_64_asm_source_files))
$(x86_64_asm_object_files): build/x86_64/%.o : src/impl/x86_64/%.asm
mkdir -p $(dir $#) && \
nasm -f elf64 $(patsubst build/x86_64/%.o, src/impl/x86_64/%.asm, $#) -o $#
.PHONY: build-x86_64
build-x86_64: $(x86_64_asm_object_files)
mkdir -p dist/x86_64 && .
x86_64-elf-ld -n -o dist/x86_64/kernel.bin -T targets/x86_64/linker.ld $(x86_64_asm_object_files) && \
cp dist/x86_64/kernel.bin targets/x86_64/iso/boot/kernel.bin && \
grub-mkrescue /usr/lib/grub/i386-pc -o dist/x86_64/target.iso targets/x86_64/iso

make: *** No rule to make target 'build-x86_64'. Stop. error in my OS when i run make build-x86_64

x86_64_asm_source_files := $(shell find src/impl/x86_64 -name *.asm)
x86_64_asm_object_files := $(patsubst src/impl/x86_64/%.asm, build/x86_64/%.o, $(x86_64_asm_source_files))
$(x86_64_asm_object_files): build/x86_64/%.o : src/impl/x86_64/%.asm
mkdir -p $(dir $#) && \
nasm -f elf64 $(patsubst build/x86_64/%.o, src/impl/x86_64/%.asm, $#) -o $#
.PHONY: build-x86_64
build-x86_64: $(x86_64_asm_object_files)
mkdir -p dist/x86_64 && \
x86_64-elf-ld -n -o dist/x86_64/kernel.bin -T targets/x86_64/linker.ld $(x86_64_asm_object_files) && \
cp dist/x86_64/kernel.bin targets/x86_64/iso/boot/kernel.bin && \
grub-mkrescue /usr/lib/grub/i386-pc -o dist/x86_64/kernel.iso targets/x86_64/iso

Function `wildcard` doesn't see $#

I have the following Makefile:
.PHONY: all clean run
INC_DIR = include
SRC_DIR = src
KERNEL_NAME = mykernel
AS = as
ASPARAMS = --32
CXX = g++
CXXPARAMS = -m32 -ffreestanding -fno-exceptions -fno-rtti -I$(INC_DIR)
#CXXPARAMS = -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fno-leading-underscore -I$(INC_DIR)
LDPARAMS = -melf_i386
BUILD_DIR = build
ISO_DIR = $(BUILD_DIR)/iso
ISO_BOOT = $(ISO_DIR)/boot
GRUB_CONFIG = $(ISO_BOOT)/grub/grub.cfg
SRC = $(wildcard $(SRC_DIR)/*.cpp)
SRC += $(wildcard $(SRC_DIR)/**/*.cpp)
SRC += $(wildcard $(SRC_DIR)/*.s)
OBJS = $(SRC:=.o)
OBJS := $(addprefix $(BUILD_DIR)/, $(OBJS))
.SECONDARY: $(OBJS)
all: $(KERNEL_NAME).iso
run: $(KERNEL_NAME).iso
qemu-system-i386 -cdrom $<
$(KERNEL_NAME).iso: $(GRUB_CONFIG) $(ISO_BOOT)/$(KERNEL_NAME).bin
grub-mkrescue -o $# $(ISO_DIR)
$(ISO_BOOT)/$(KERNEL_NAME).bin: $(SRC_DIR)/linker.ld $(OBJS)
ld $(LDPARAMS) -T $^ -o $# -nostdlib
$(GRUB_CONFIG):
mkdir -p $(dir $#)
echo 'set timeout=0' > $(GRUB_CONFIG)
echo 'set default=0' >> $(GRUB_CONFIG)
echo '' >> $(GRUB_CONFIG)
echo 'menuentry "$(KERNEL_NAME) OS" {' >> $(GRUB_CONFIG)
echo ' multiboot2 /boot/$(KERNEL_NAME).bin' >> $(GRUB_CONFIG)
echo ' boot' >> $(GRUB_CONFIG)
echo '}' >> $(GRUB_CONFIG)
$(BUILD_DIR)/%.cpp.o: %.cpp
echo $(dir $#)
echo $(wildcard (dir $#))
#ifeq ($(wildcard (dir $#)),)
# echo "mkdir -p $(dir $#)" ;
# $(CXX) $(CXXPARAMS) $< -c -o $#
#endif
$(BUILD_DIR)/%.s.o: %.s
#if [ ! -d $(dir $#) ]; then \
echo "mkdir -p $(dir $#)" ; \
mkdir -p $(dir $#) ; \
fi
$(AS) $(ASPARAMS) $< -o $#
clean:
rm -rf $(KERNEL_NAME).iso $(BUILD_DIR)
Why do I get the output with empty echo by running make? The directory build/src exists.
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/
build/src/
echo
echo build/src/gui/
build/src/gui/
echo
echo build/src/gui/
build/src/gui/
echo
ld -melf_i386 -T src/linker.ld build/src/Keyboard.cpp.o build/src/VGArray.cpp.o build/src/mykernel.cpp.o build/src/TextCursor.cpp.o build/src/VideoContext.cpp.o build/src/GDTable.cpp.o build/src/gui/DrawContext.cpp.o build/src/gui/Window.cpp.o build/src/loader.s.o -o build/iso/boot/mykernel.bin -nostdlib
ld: cannot find build/src/Keyboard.cpp.o: No such file or directory
ld: cannot find build/src/VGArray.cpp.o: No such file or directory
ld: cannot find build/src/mykernel.cpp.o: No such file or directory
ld: cannot find build/src/TextCursor.cpp.o: No such file or directory
ld: cannot find build/src/VideoContext.cpp.o: No such file or directory
ld: cannot find build/src/GDTable.cpp.o: No such file or directory
ld: cannot find build/src/gui/DrawContext.cpp.o: No such file or directory
ld: cannot find build/src/gui/Window.cpp.o: No such file or directory
Makefile:33: recipe for target 'build/iso/boot/mykernel.bin' failed
make: *** [build/iso/boot/mykernel.bin] Error 1
I tried to make a minimal example, but it works right when build directory exists.
.PHONY: all clean
CXX = g++
OPTS = -Wall -Wextra
BUILD_DIR = build
APP = $(BUILD_DIR)/main
SRC_DIR = src
SRC = $(wildcard $(SRC_DIR)/*.cpp)
all: $(APP)
$(APP): $(APP).o
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
echo $(dir $#)
echo $(wildcard $(dir $#))
clean:
rm -rf $(BUILD_DIR) main *.o
Replace echo $(wildcard (dir $#)) by echo $(wildcard $(dir $#)) and things should work as you expect.
Note: SRC += $(wildcard $(SRC_DIR)/**/*.cpp) will not recurse in subdirectories. If you believed it would, try:
SRC = $(shell find $(SRC_DIR) -type f -name '*.cpp')
SRC += $(wildcard $(SRC_DIR)/*.s)

Makefile to compile lists of source files

I have lists of files that I want my Makefile to compile, one list for each source language:
CFILES= Src/Application/main.c Src/Core/data.c Lib/routines.c
ASFILES= Src/Application/startup.s Lib/sqrt.s
I want all the output in one directory:
OBJDIR= output
How do I do the equivalent of:
output/main.o : Src/Application/main.c
cc -c -o output/main.o Src/Application/main.c
output/data.o : Src/Core/data.c
cc -c -o output/data.o Src/Core/data.c
output/routines.o : Lib/routines.c
cc -c -o output/routines.o Lib/routines.c
output/startup.o : Src/Application/startup.s
as -o output/startup.o Src/Application/startup.s
output/sqrt.o : Lib/sqrt.s
as -o output/sqrt.o Lib/sqrt.s
The recipes are the same for every file in its list.
The input files are in all sorts of different directories and it is not acceptable to just list their filenames and use a search path to find them, their explicit paths must be used.
The output filename is the basename of the source file name with the extension changed to o. There are no duplicated basenames between the lists for the different source languages.
I do not want to have to list the object files, this should be derived from the source lists.
I am using gnu make, but bonus points for a portable solution.
Something like the following could do:
all :
OBJDIR := output
CFILES := Src/Application/main.c Src/Core/data.c Lib/routines.c
ASFILES := Src/Application/startup.s Lib/sqrt.s
target = ${OBJDIR}/$(patsubst %.s,%.o,$(notdir ${1}))
obj.c :=
obj.s :=
define obj
$(call target,${1}) : ${1} | ${OBJDIR}
obj$(suffix ${1}) += $(call target,${1})
${1} : ; mkdir -p `dirname $$#` && touch $$# # Create the source for testing. Remove this.
endef
define SOURCES
$(foreach src,${1},$(eval $(call obj,${src})))
endef
$(eval $(call SOURCES,${CFILES}))
$(eval $(call SOURCES,${ASFILES}))
all : ${obj.c} ${obj.s}
${obj.c} : % :
#echo cc -c -o $# $^; touch $# # echo and touch are for testing. Remove these.
${obj.s} : % :
#echo as -o $# $^; touch $# # echo and touch are for testing. Remove these.
${OBJDIR} :
mkdir $#
.PHONY: all
Output:
$ make
make: Entering directory '/home/max/tmp'
mkdir -p `dirname Src/Application/main.c` && touch Src/Application/main.c # Create the source for testing. Remove this.
mkdir output
cc -c -o output/main.c Src/Application/main.c
mkdir -p `dirname Src/Core/data.c` && touch Src/Core/data.c # Create the source for testing. Remove this.
cc -c -o output/data.c Src/Core/data.c
mkdir -p `dirname Lib/routines.c` && touch Lib/routines.c # Create the source for testing. Remove this.
cc -c -o output/routines.c Lib/routines.c
mkdir -p `dirname Src/Application/startup.s` && touch Src/Application/startup.s # Create the source for testing. Remove this.
as -o output/startup.o Src/Application/startup.s
mkdir -p `dirname Lib/sqrt.s` && touch Lib/sqrt.s # Create the source for testing. Remove this.
as -o output/sqrt.o Lib/sqrt.s
make: Leaving directory '/home/max/tmp'
I have little experience in writing makefiles, so this is just an attempt. In my example I have C and C++ files in a few directories and build a program made of these files.
$ cat Makefile
.PHONY : clean all
CC=gcc
CXX=g++
CFILES = c/f.c c/g.c
CPPFILES = cpp/main.cpp
OUTPUT = ./output
SOURCE_DIRS := $(dir $(CFILES))
SOURCE_DIRS += $(dir $(CPPFILES))
VPATH = $(sort $(SOURCE_DIRS))
C_FILENAMES := $(notdir $(CFILES))
CPP_FILENAMES += $(notdir $(CPPFILES))
OBJ_FILES := $(patsubst %.c, $(OUTPUT)/%.o, $(C_FILENAMES) )
OBJ_FILES += $(patsubst %.cpp, $(OUTPUT)/%.o, $(CPP_FILENAMES) )
all : $(OUTPUT)/program
$(OUTPUT)/program : $(OBJ_FILES)
g++ -o $# $^
$(OUTPUT)/%.o : %.cpp
$(shell mkdir -p $(OUTPUT) )
$(CXX) $(CXXFLAGS) -c $< -o $#
$(OUTPUT)/%.o : %.c
$(shell mkdir -p $(OUTPUT) )
$(CC) $(CFLAGS) -c $< -o $#
clean:
rm -fr $(OUTPUT)
And this is an example of using my makefile:
$ make
gcc -c c/f.c -o output/f.o
gcc -c c/g.c -o output/g.o
g++ -c cpp/main.cpp -o output/main.o
g++ -o output/program output/f.o output/g.o output/main.o
The following method should do what you want: compile all of your specified source files and put the object files in the directory ./output automatically. Of course, you need to provide compiler options, proper libraries necessary for linking, and so on.
OBJDIR =./output
SRCDIR1 =./Src/Application
SRCDIR2 =./Src/Core
SRCDIR3 =./Lib
SRC1 =$(SRCDIR1)/main.c
SRC2 =$(SRCDIR2)/data.c
SRC3 =$(SRCDIR3)/routines.c
SRC4 =$(SRCDIR1)/startup.s
SRC5 =$(SRCDIR3)/sqrt.s
OBJ1 =$(patsubst $(SRCDIR1)/%.c,$(OBJDIR)/%.o,$(SRC1))
OBJ2 =$(patsubst $(SRCDIR2)/%.c,$(OBJDIR)/%.o,$(SRC2))
OBJ3 =$(patsubst $(SRCDIR3)/%.c,$(OBJDIR)/%.o,$(SRC3))
OBJ4 =$(patsubst $(SRCDIR1)/%.s,$(OBJDIR)/%.o,$(SRC4))
OBJ5 =$(patsubst $(SRCDIR3)/%.s,$(OBJDIR)/%.o,$(SRC5))
vpath %.c $(SRCDIR1): $(SRCDIR2): $(SRCDIR3)
vpath %.s $(SRCDIR1): $(SRCDIR3)
all: $(OBJ1) $(OBJ2) $(OBJ3) $(OBJ4) $(OBJ5)
cc $^ -o executable
$(OBJDIR)/%.o: %.c | $(OBJDIR)
cc -c $< -o $#
$(OBJDIR)/%.o: %.s | $(OBJDIR)
cc -c $< -o $#
$(OBJDIR):
mkdir -p $(OBJDIR)

Insert dependency in parallel makefile

I'm trying to add an extra dependency to a rule in a parallel makefile. I might have found a way, but I'm in doubt. (I haven't written the original makefile and I'm not an expert in make.)
The original makefile looks like this:
VER = busybox-1.16.2
URL = http://busybox.net/downloads/$(VER).tar.bz2
export KBUILD_OUTPUT = $(ROOTDIR)/user/busybox/build-$(VER)
all: build-$(VER)/.config depmod.pl
$(MAKE) -C build-$(VER)
build-$(VER)/.config: $(ROOTDIR)/config/.config
mkdir -p build-$(VER)
sed -n \
-e '/_CROSS_COMPILER_PREFIX=/s:=.*:="$(CROSS_COMPILE)":' \
-e '/CONFIG_USER_BUSYBOX_/s:CONFIG_USER_BUSYBOX_:CONFIG_:p' \
$< > $#.uclinux-dist.new
set -e ; \
if [ ! -e $# ] || ! cmp -s $#.uclinux-dist.new $#.uclinux-dist.old ; then \
cp $#.uclinux-dist.new $#.uclinux-dist.old ; \
cp $#.uclinux-dist.old $# ; \
yes "" | $(MAKE) -C $(VER) oldconfig ; \
fi
depmod.pl: $(VER)/examples/depmod.pl
ln -sf $< $#
I want to add a 'download' rule to the make.
$(VER)/: $(VER).tar.bz2
tar -jxvf $(VER).tar.bz2
touch $#
$(VER).tar.bz2:
wget $(URL)
touch $#
This rule must be executed before anything else. The parallel build prevent constructs like
all: |$(VER)/ build-$(VER)/.config depmod.pl
(This works in single threaded builds.)
My solution so far is this:
VER = busybox-1.18.5
URL = http://busybox.net/downloads/$(VER).tar.bz2
export KBUILD_OUTPUT = $(ROOTDIR)/user/busybox/build-$(VER)
all: build-$(VER)/.config depmod.pl
$(MAKE) -C build-$(VER)
$(VER)/: $(VER).tar.bz2
tar -jxvf $(VER).tar.bz2
touch $#
$(VER).tar.bz2:
wget $(URL)
touch $#
build-$(VER)/.config: $(ROOTDIR)/config/.config | $(VER)/
mkdir -p build-$(VER)
sed -n \
-e '/_CROSS_COMPILER_PREFIX=/s:=.*:="$(CROSS_COMPILE)":' \
-e '/CONFIG_USER_BUSYBOX_/s:CONFIG_USER_BUSYBOX_:CONFIG_:p' \
$< > $#.uclinux-dist.new
set -e ; \
if [ ! -e $# ] || ! cmp -s $#.uclinux-dist.new $#.uclinux-dist.old ; then \
cp $#.uclinux-dist.new $#.uclinux-dist.old ; \
cp $#.uclinux-dist.old $# ; \
yes "" | $(MAKE) -C $(VER) oldconfig ; \
fi
depmod.pl: $(VER)/examples/depmod.pl
ln -sf $< $#
$(VER)/examples/depmod.pl: | $(VER)/
Problem is, I don't really know what kind of magic the depmod.pl rule is. Is it executed correctly, now that I've added an explicit empty rule?
I'd hate to answer my own question, but I think I've found the answer.
The dependency of the depmod.pl rule is not real/relevant in the original script.
The code:
depmod.pl: $(VER)/examples/depmod.pl
ln -sf $< $#
Ought to be written:
depmod.pl:
ln -sf $(VER)/examples/depmod.pl $#
So my extra empty rule makes no difference. In the new script, the dependency almost makes sence though.

Resources