I'm modifying a makefile and a bit in the dark. I've tried googling and reading make primers but nothing has obvious answers to my problems below
Its first target: "default" compiles and builds using the gcc_riscv compiler
It has 2 phony targets: "clean" and "info". clean does what it should and info invokes riscv_readelf
I want to add 2 targets: "gccx86" and "lint" that will do the same as "default" except invoke the x86 compiler or lint largely on the same file list.
gccx86 will swap a few "hardware close" modules for mocks, and tweak the include hierarchy to make the whole codebase compile as a linux target instead. Hmmm maybe I should call it linux?
But anyway I want my 2 new targets to take rougly the same file list. The $(RISCV-GCC) symbol will point to gcc for "gccx86", and clang-tidy for "lint"
Basically how do I declare these 2 new targets, and conditionally declare the include path and other stuff based on the target
I guess it should be along the lines of:
export CROSS_PREFIX ?= /opt/cae/riscv-gcc/riscv_gcc12.2.1/bin/riscv64-unknown-elf-
export RISCV_GCC ?= $(CROSS_PREFIX)gcc
ifeq ($(TARGET_VARIABLE), "gccx86")
export RISCV_GCC = gcc
endif
ifeq ($(TARGET_VARIABLE), "lint")
export RISCV_GCC = clang-tidy
endif
Well what is the TARGET_VARIABLE?
Or is multiple targets wrong and I should have a -lint switch: make -lint? How do I pick up such switches if so.
Eventually we'll throw out the whole mess and go cmake but not over the next few months
2 Answers 2
The $(RISCV-GCC) symbol will point to gcc for "gccx86", and clang-tidy for "lint"
If you're willing to be specific to GNU make, then you appear to be looking for target-specific variables. The concept is very similar to what you describe, though the syntax is different. It would be something like:
export RISCV_GCC ?= $(CROSS_PREFIX)gcc
gccx86: export RISCV_GCC = gcc
lint: export RISCV_GCC = clang-tidy
# ...
Under some circumstances, prerequisites will inherit those target-specific variables from the targets that depend on them, but do read the linked docs for a full explanation.
Or is multiple targets wrong and I should have a -lint switch: make -lint? How do I pick up such switches if so.
Multiple targets is probably the best way to go, but you should question instead whether to implement that by varying the definition of the compiler binary as you propose to do. Consider instead having your various top-level phony targets build distinct physical targets. That they do so based on the same sources would be well addressed by defining the source list(s) via variables, so that you can avoid duplication by reusing those variables instead of by relying on a common back-end compilation target (as I assume the compiler-redefinition approach is aimed at). This has numerous practical advantages, large among them avoiding any confusion over how particular physical targets were built.
Comments
I might suggest something like this:
CC_default := $(CROSS_PREFIX)gcc
CC_gccx86 := gcc
CC_lint := clang_tidy
BASE_FILES := a.o b.o c.o
OBJS_default := $(addprefix objs_default/,$(BASE_FILES))
OBJS_gccx86 := $(addprefix objs_gccx86/,$(BASE_FILES) gcx86.o)
OBJS_lint := $(addprefix objs_lint/,$(BASE_FILES) lint.o)
CFLAGS_lint := -O1
CFLAGS_DEFAULT := -O2
define RULES
1ドル : $$(OBJS_1ドル)
$$(CC_1ドル) -o $$@ $$(OBJS_1ドル) $$(or $$(CFLAGS_1ドル),$$(CFLAGS_DEFAULT))
$$(OBJS_1ドル): objs_1ドル/%.o: src/%.c
$$(CC_1ドル) $$(or $$(CFLAGS_1ドル),$$(CFLAGS_DEFAULT)) $$@ $$<
endef
# uncomment to debug:
# $(info $(call RULES lint))
$(eval $(call RULES default))
$(eval $(call RULES gccx86))
$(eval $(call RULES lint))
This creates a new rule for each of your possible targets, and makes it simple to tweak the rules of each. The problem with using target-specific variables as you suggested is that it introduces a whole host of sharp sticks that you can trip on -- for example, if you have overlapping prerequisites between two targets, then you can't guarantee that the prerequisite was built with the correct options. Also the behavior becomes murky when you try to build two targets in the same make invocation (i.e. make default lint).
This makes heavy use of computed variable names to reduce the ifdefs. It unfortunately uses eval, but in this case its use is likely justified.
One trick here with the computed variable names is to use the $(or $(TARG_OPT),(DEFAULT_OPT)), trick -- if a target specific option is not specified, it can drop back to a default option.
make mode=whateverand switch on$(mode)in the Makefile.make risc gcc. You might want to read more about target specific variables:when you define a target-specific variable that variable value is also in effect for all prerequisites of this target. Meaning you could set variables on the phony targets and have them apply downward. gnu.org/software/make/manual/html_node/Target_002dspecific.html