1

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
  • Probably simplest is the format make mode=whatever and switch on $(mode) in the Makefile. Commented Aug 26 at 12:42
  • 1
    The compilers should generate different target and intermediate files (in different directories) so you can build both at the same time like 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 Commented Aug 26 at 16:01

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.