The makefiles with uCR use the target string to select files that are target specific. It does this by including the target name in certain include paths, including target specific makefile sections, and including the target name in some source file names.
First, the publicly accessible files in uCR are seperated by directory structure. At the root of the uCR tree, there are several directories. The source, include, etc. directories are common directories and include the files common to all the targets. There are also many directories named i960_ise, i386_linux, etc. These directories have the include and lib structures that are unique for each target.
The compilation rules in make/rules.mk cause the compile commands to include ``-I$(TARGET)/include'' in the include path and the link commands to include ``-L$(TARGET)/lib'' in the link path. Thus, headers and libraries that are different between targets are placed in seperate directories.
Some header files have parts that are reasonably shared by several targets. For example, the i960_cyclone and i960_ise targets both have i960Jx CPUs so some header file contents can be shared. uCR source manages this by placing the common parts in the common include directory under a descreet directory, say ``include/i960.'' The target specific parts of setjmp, for example, could include <i980/setjmp_asm.h> to get all the code common to i960 CPUs.
Within the common source directories, there are also target specific makefile segments. These allow libraries to include some extra object modules for certain targets. For example, in libucr, the i960_ise.mk file is included by the Makefile and lists the object files that are needed specifically by i960 targets.
TARGET_OBJ := _exit-abort.o misc-i960.o pause-i960Jx.o setjmp-i960.o \ thread_regs-i960.o uCR_fault-i960.o uCR_isr-i960.o \ uCR_stack-i960.o uCR_switch-i960.o
The i960_cyclone.mk is very similar, but uses slightly different versions of a few files:
TARGET_OBJ := _exit-mon960.o misc-i960.o pause-i960.o setjmp-i960.o \ thread_regs-i960.o uCR_fault-i960.o uCR_isr-i960.o \ uCR_stack-i960.o uCR_switch-i960.o
Notice that the Cyclone target uses mon960 for the exit, and a non-Jx version of pause.
This technique of keeping code for different targets in different source files keeps the source files relatively free of conditional compilation macros and makes the source easier to read as a consistent whole.
There is yet another way to seperate source files, that uses make rules. The uCR makefile rules include a pattern rule for generating file foo.o from foo.cc and foo-$(TARGET).cc. Thus, if every target has different source for abort.o, then instead of putting it in abort.cc, put it into abort-i960_ise.cc and abort-i386_linux.cc, etc. This technique works automatically for C, C++ and assembly source files.
The uCR make rules allow you to provide a generic implementation of some file, say strcpy.c, and a target specific implementation for the few targets where you need specific optimizations, say strcpy-i386_linux.s. The uCR make rules will first look for a target specific source of any type, and use a generic source file if necessary. This allows one to get started with a new target relatively quickly by using unoptimized default implementations of things.