Rich Neswold Carl Schumann
$Date: 2000/01/04 20:42:11 $
This document describes an include file for the make facility that helps standardize our development environment.
The fe.mk include file was developed to improve the build environment for front-end programmers. Wind Rivers has provided a nice library of make include files, but it takes discipline to use it effectively. fe.mk provides a consistent use of these files, as well as hiding the gory details.
This section takes you through a real-world example of using the fe.mk include file.
Before using fe.mk, you must correctly set up your Unix environment. This is easily accomplished using the env_ppc, env_68k, and env_68020 commands1. These commands prepare environment variables and search paths to refer to the appropriate Tornado tools. The env_ppc command sets up the environment for the most common PowerPC processor, currently the PPC603. The env_68k, sets up the envitronment variables for the most common 68000 family processor, currently the MC68040. The env_68020 sets up the environment for the MC68020.
The first step in building your makefile is easy; simply include fe.mk. Make sure you are reading it from the products directory. That way your makefile can benefit from bugs fixes and enhancements. Your makefile will always start out like this:
At this point, the file is effectively 100+ lines long, since it is pulling in the Wind Rivers recommended rules and symbols. These rules can be further customized by defining symbols in your makefile. In order to do this, the symbols must be defined before fe.mk is read.
One symbol that always needs to be defined is MOD_TARGETS. This symbol tells the makefile what modules are going to be produced. In the case of this example front-end, only one module file is going to be produced: mdat.out. The out extentsion indicates that this file is ready to be dynamically loaded, i.e., has been munched 2. After adding this line, Makefile becomes:
MOD_TARGETS = mdat.out include $(PRODUCTS_INCDIR)/fe.mk
Pretty simple, so far. However, we've only told make what to build, not how to build it. We could have made fe.mk simply compile all the C and C++ files in the current directory and link them together, but this would have not been the most flexible solution. It wouldn't, for instance, allow multiple modules to be built3.
Since we don't want make to assume which source files to use, we need to specify them. These rules should be defined after fe.mk is included. For every target mentioned in MOD_TARGETS, we need to add one rule that shows the dependancies. Since make also requires you to tell it how to build the target, a command line needs to be added. Fortunately, fe.mk defines a macro which correctly builds a VxWorks module -- $(make-mod). This command will preform the needed munch.
Getting back to our example, mdat.out is built up using inient.o, ooc_ini.o, task_15hz.o, and mdat_gpip.o. Adding two more lines tells make how to build it:
MOD_TARGETS = mdat.out include $(PRODUCTS_INCDIR)/fe.mk mdat.out : inient.o ooc_ini.o task_15hz.o mdat_gpip.o $(make-mod)
This should be the complete makefile. Typing make at a shell prompt will build mdat.out.
Since this front-end uses the MOOC framework, loading just this module onto a VxWorks machine will generate a lot of unresolved references. The functions for MOOC (and ACNET), need to be loaded first. They can either be loaded from the products directory or they can be linked into the module. The front-end programmer for this example system decided to link with the libraries, creating a monolithic module containing the entire application. Additional libraries are part of the dependancy list. You don't need to specify the entire path; if make can't find the library in the current directory, it will look in the products directory.
The final Makefile becomes:
MOD_TARGETS = mdat.out include $(PRODUCTS_INCDIR)/fe.mk mdat.out : inient.o ooc_ini.o task_15hz.o mdat_gpip.o \ libftpm.a libmooc_swap.a libtrig.a libtrigvme.a \ libacnet.a libssm.a $(make-mod)
Note that fe.mk accepts other target symbols besides MOD_TARGETS. LIB_TARGETS indicats libraries or monolithic objects which should be built. Two more targets HEADER_TARGETS and OTHER_TARGETS, indicate files which are not generated, but do need to be installed somewhere outside of the build/working directory. HEADER_TARGETS is intended for include files for make or C/C++. OTHER_TARGETS is intended for other non-generated files, e.g., vxWorks startup scripts. All of these targets indicate to fe.mk that the files need to be installed outside of the build/working directory. fe.mk only supports installation of targets for front-end products and other projects which explicitly request it, by defining the symbol CVSTAGS. This is discussed later. If you do not define CVSTAGS, then fe.mk ignores HEADER_TARGETS and OTHER_TARGETS.
Sometimes you need to specify an additional compiler option. You can add options by defining them with the ADDED_CFLAGS and ADDED_C++FLAGS (I hope it's obvious what each of these does!) For instance, to turn all warnings on, you would place this in your makefile (before the fe.mk inclusion):
ADDED_CFLAGS = -Wall
If you need to specify options specific to a particular processor you may also use processor specific symbols, e.g, ADDED_PPC603_CFLAGS. Hopefully, this will be uncommon.
The linker doesn't have too many useful options, and fe.mk already specifies the ones necessary to build a module. But if you find yourself needing to add an option, use the LD_PARTIAL_FLAGS symbol before the inclusion of fe.mk.
If you are trying to build a library instead of an object module, you can use the $(make-lib) macro, rather than $(make-mod). You should also use the LIB_TARGETS symbol instead of the MOD_TARGETS symbol.
You can build a debug version of your module by defining the DEBUG symbol when invoking make. I use the following:
The debug version gets compiled with the '-g' option (include debug information) and no optimizations. Also, a debug version will have assert macros compiled in, as well as logDebug() calls (if you're using the errlog facility.)
When make is run in a clean directory, fe.mk will first create dependancy files for all the C and C++ files in the current directory. These files have the same base name as the source file with a .d extension. These files are used by fe.mk to determine when source files need to be recompiled due to a header file modification.
This target is provided by fe.mk. It cleans the current directory of files that can be regenerated by running make.
This include file has only been used in projects that exist in a single base directory. Little thought has been done in getting it to work with projects containing subdirectories. This hasn't appeared to be a problem.
fe.mk CVS tags installed releases automatically.
fe.mk embeds the tag/release in generated object automaticaly. This information can be retrieved from a running system which has loaded an object file as well as from the file itself if it is just sitting on a file system somewhere.
You can retrieve the tag information from a file via the what or ident command. The overall project version is embedded with the CVS keyword "Source". What and ident are standard SCCS and CVS tools. Check their man pages for more information.
On a running system the version for a particular project is retrievable via a routine that returns a pointer to the version string embedded in the object. There is one routine per project. The name of the routine is formed by concatenating a standard prefix along with the location of the project in the CVS repository, with slashes replaced by double underscores. The standard prefix is "ident__". Therefore, e.g., acnet which is in the repository at fermi/fe_products/acnet, will have the identification routine ident__fermi__fe_products__acnet. To display the version of acnet using this routine, you can enter the following at the shell prompt printf(ident__fermi__fe_products__acnet()).
When you invoke the install target, fe.mk will install a new beta or overwrite the existing one. It will not allow you to overwrite existing stable releases. When the beta has been tested and accepted it can be designated stable by invoking the promote target.
fe.mk installs files with a suffix on the root filename that indicates the release of the project, e.g., stuff-1.0.out. This allows multiple stable releases of a project to be kept in a single directory.
fe.mk maintains links that reference the release which is currently designated the current beta and the current stable. Since a beta may not exist, it also maintains a latest link which is the latest release installed, the beta if one is present otherwise the current stable.
fe.mk in collaboration with CVS's loginfo collects log messages in the directory hierachy /home/ad_projects/cvslogs. This hiearchy parallels the repostiory hiearchy. Each directory in the repository will have a directory in the log hiearchy. In this log directory the file __LogEntries will maintain a concatenation of all commits, as wells as entries for installs and promotions for that particular project.
fe.mk manages all of this for projects which are built for multiple processors. Principley, this is abouting supporting different install directories for each processor and keeping the tag(s) for each release correct for all processors since the processor's releases are not all built and installed at the same time.
The project must be maintained in CVS on crusher.
The project's Makefile must define some make variables which tell fe.mk what to do.
VID indicates the version of the release, e.g., 2.1.
CVSTAGS tells fe.mk that the project statifies the assumptions and that it should provide the additional functionality. The value of this variable is unimportant.
SUPPORTED_CPUS indicates the CPUs for which this project is built, e.g., MC68040 PPC603. Typically, this will probably be a single processor.
Four make variables are used to indicate where to install stuff. INSTALL_MODDIR is where to install modules (*.out) INSTALL_LIBDIR is where to install libraries (*.a) INSTALL_INCDIR is where to install headers (*.h) INSTALL_OTHDIR is where to install other stuff, e.g., vxWorks startup scripts and OS object files. These variables should probably all look something like /fecode-bd/vxworks_boot/fe/vacuum. For projects which are built for multiple processors the install directories for modules and libraries can be specified in terms of cpu by using $$CPU where one would like to substitute the actual CPU. For example, /fecode-bd/vxworks_boot/fe/vacuum/module/$$CPU, would expanded to /fecode-bd/vxworks_boot/fe/vacuum/module/MC68040 if your environment variables select the MC68040.
The following Makefile (for vaccum) is an example of using fe.mk with the additional functionality. Note that this Makefile also build the vWorks OS, which accounts for a major portion of its length.
# $Id: fe_make.sgml,v 1.9 2000/01/04 20:42:11 schumann Exp $ VID = 2.1 CVSTAGS = 1 SUPPORTED_CPUS = MC68040 INSTALL_DIR = /fecode-bd/vxworks_boot/fe/vacuum INSTALL_LIBDIR = $(INSTALL_DIR) INSTALL_INCDIR = $(INSTALL_DIR) INSTALL_OTHDIR = $(INSTALL_DIR) INSTALL_MODDIR = $(INSTALL_DIR) ADDED_CFLAGS = -I. -fvolatile -Werror ADDED_MC68040_CFLAGS = MOD_TARGETS = shell.out vxWorks OTHER_TARGETS = startup_mivac.cmd startup_pbvac.cmd startup_vxtstx.cmd \ vxWorks.sym include $(PRODUCTS_INCDIR)fe.mk CORE_OBJS = arcn_nodes.o arcnet.o debug_utils.o inient.o nodedev.o ooc_ini.o \ ssm_tod.o v_access.o viob.o viob_create.o swversion.o cathode.o ionpump.o \ ionpumpavg.o manifold.o pirani.o roughing.o sector.o roughing_sas.o \ subpump.o roughing_sa.o yaknet.o miscdev_viob.o shell.out : $(CORE_OBJS) \ libmooc_noswap.a \ libacnet.a \ libtrigvme.a \ libtrig.a \ libftpm.a \ libssm.a $(make-mod) vxWorks : $(WIND_BASE)/target/config/mv162/.include-fermi-162-main-injector-vacuum launch cp $(WIND_BASE)/target/config/mv162/vxWorks . cp $(WIND_BASE)/target/config/mv162/vxWorks.sym . cleandir :: rm -f vxWorks.sym vxWorks_mivac.sym
The install target installs the targets in the working directory into the install directories as a beta. Before doing an install you should do a cleandir, confirm that you have executed the setup command for the processor you intended to install, and then build the project to insure you do not install objects for the wrong processor, e.g, installng PPC603 binaries into the MC68040 area.
The beta.install target installs the targets for all supported processors. It does this by looping over all supported processors. For each processor it does a cleandir followed by an install. This automation makes it easier to install a beta and eliminates the possiblity of installing a target built for one processor into the install area of another processor.
The promote target designates the current beta stable. This is done simultaneously for all supported cpus. The promotion is done by updating the links entirely within the install area, the working directory objects are not involved.
These are actually shell aliases that run a shell script.
Objects are munched to insure that file scope C++ objects are constructed, i.e., initialized.
One example is ACNET. It has a library which consists of almost all of the source files. A few files, though, are only used by extra modules that virtually nobody uses. We don't want these to be linked into every target in the ACNET project.