Intel



“make” Tips and Tricks for Nios® II

Dec 20, 2010 cruben

Introduction

Altera Corp. introduced the Nios® II Software Build Tools (SBT) in 2009. The SBT automatically create custom makefiles for your custom application from either the GUI or the command line. This is exactly what most Nios® II users need. However, there are cases where you need a little more control over the make process, but you prefer not to make a custom makefile from scratch. For example, if you have a file which uses the __DATE __ and __TIME__ C macros or build numbering system, you may want them to be rebuilt every time you compile or just when another file changes. These and other make topics will be explored in this paper. This paper assumes that you have some knowledge of makefiles and the Nios® II SBT. A great make reference book is “Managing Project with make”. It is published by O’REILLY and written by Andrew Oram & Steve Talbot.

GNU make

The Nios® II compiler and binary tools are all GNU based. So it follows that the “make” utiliy is GNU too. When “make” is called, the make utility automatically looks for the following make script files in this order; GNUmakefile, makefile and Makefile. When you create a project with the SBT, the SBT automatically creates a standard Makefile for both the application and BSP projects. Modifying these files by hand is not recommended because the Application or BSP Makefile gets regenerated occasionally, overwriting your changes you made. A better approach would be creating your own custom GNUmakefile and then call the standard SBT generated Makefile in your GNUmakfile. GNU make will automatically use the GNUmakefile if it is present. This behavior allows us to override the default behavior of builds without changing the build flow from the GUI or the command line. Here is an extremely simple GNUmakefile example.

# GNUmakefile in the application directory

# Example 1

%:

$(MAKE) -f Makefile $@

A little explanation may be needed for the new make user. Make really only knows about two things: targets (the thing to build) and rules (how to build it). A target and its’ dependencies are separated by a “:” and must start a line. One makefile may contain multiple targets. Rules follow the target and are always indented by at least one Tab ( do not use spaces). In Example 1 “%” is the target. “%” is a variable which is the input parameter to make. There are no dependencies in this example so it will always run the rules, and there is just one rule to make the target. If from the command line you typed

make fred

make would search through all the targets in the makefile looking for a target called “fred”. It will look at the dependency associated with target “fred” and decide if it needed to be rebuilt. If it does, it will execute the rules listed for that target. Example 1 has only one target, “%”, which matches anything that is passed in to make, in this case “fred”. “$@” is shorthand for current target name (“fred”again). Summarizing, this makefile simply calls make with the “Makefile” file and with the argument passed in to make. In other words it behaves the same as if GNUmakefile were not present.

Adding Prebuild and Postbuild steps to GNUmakefile, and Makefile

# GNUmakefile in the application directory

# Example 2

%:

touch test.c

$(MAKE) -f Makefile $@

cp *.elf current/

In Example 2 we added two more rules to the target. The first rule “touch test.c” simply forces the file time stamp to the current time and date. This makes the file appear to make as if it has changed. The next rule “$(MAKE) –f Makefile $@” will recompile the target “%” and if test.c is a dependence of that target, test.c will get recompiled. The third rule will copy the .elf file to the “current/” subdirectory. Why would you want to do something like this? If test.c uses the __DATE__, or __TIME__ C macros, make has no idea that test.c needs to be recompiled every time. To overcome this, we add the touch command to force a recompile of that file.

The “touch” and “cp” in Example 2 can be thought of as prebuild and postbuild steps. The SBT also contains the ability to add prebuild and postbuild steps in the standard Makefile. For the application Makefile there is no GUI for settings these options. It must be done through the command line with the following commands.

BUILD_PRE_PROCESS—This variable allows you to specify a command to be

executed prior to building the application, for example,

cp *.elf ../lastbuild.

■ BUILD_POST_PROCESS—This variable allows you to specify a command to be

executed after building the application, for example,

cp *.elf //production/test/nios2executables.

For more details, see the Nios® II Software Developer’s Handbook.

The BSP Makefile can have the prebuild and postbuild processes set with the above command or with the BSP editor. To get to these settings open the BSP-editor and go to the “main” tab. Under the advanced settings, look for build_post_process and build_pre_process.

[pic]

Since the ability to create a pre and post build steps exists in the SBT, why would you create your own GNUmakefile? In general either way will work. The advantage GNUmakefile is its simplicity. It is very easy to add multiple lines of pre and post build commands and more complicated makes like Example 5, may be better suited to the GNUmakefile process.

Using shell scripts

One of the biggest disadvantages of makefiles is the inability to use conditional operators like if, then, and else. Make simply executes everything in order until an error occurs. To overcome this limitation, most developers use shell scripts.

Note: Actually GNU make does have some “if” constructs but they are limited and not standard to all make systems.

Example 3 shows how to run a shell script from within make.

# GNUmakefile in the application directory

# Example 3 shell script calling

%:

sh make_buildnum.sh $@

$(MAKE) -f Makefile $@

Scripts can be very versatile and extremely complex. A prebuild script, for the BSP, could look and verify that custom drivers are installed in the drivers subdirectory, or build a custom library. A prebuild script for the application could generate a build number or update a time and date in a header or C file. A postbuild could build the target install_mem_init or other custom output files.

Including the original Makefile

If you want to add special make targets that are dependent on variables created by the SBT generated Makefile, you can include that Makefile. (See line 3 of Example 4)

#GNUmakefile in the application directory

# Example4

include Makefile

build_number.h: $(APP_OBJS) $(LINKER_SCRIPT) $(APP_LDDEPS)

@echo

@echo Generating build number 0..

sh make_buildnum.sh

my_flash: $(ELF)

@echo

@echo Generating build number 1..

sh make_flash.sh

The “include” adds the entire contents of the Makefile right at the “include” location, allowing you to use variables and parameters which are defined in the original SBT generated Makefile. This is ideal if you want to add a new target that builds the flash files or custom formatted file for the software and/or hardware.

Note: In Example 4, build_number.h cannot be one of the source files. If it is, make will detect a circular dependency and start throwing dependencies away to resolve the situation.

Peculiarities of SBT Makefiles.

Make has a really handy feature that allows a script to determine if there is anything that needs to be built. It is the –q option.

make –q –f Makefile all

It should return a 0 if it is all up to date and a positive number if there is something that needs to be recompiled. Unfortunately, because the list of objects to be compiled is created on the fly in SBT Makefiles, the –q and –n options always return a 1. So they are useless in letting us know if the target needs to be compiled.

Example: Making a Build Number Generator

Creating a build number generator will pull together all the concepts that we have discussed so far. It will use a custom GNUmakefile and use a custom shell script. The GNUmakefile for Example 3 is the same code we will use for our build number example. The make_buildnum.sh shell script is found in Appendix A.

In Example 3 whenever make is called the “%” target in the GNUmakefile will be used. The rules consist of two steps. First call make_buildnum.sh then call the standard make. Notice that the $@ ( target name) is passed to both the script and the make. The script could look at the target name and only conditionally run the incrementing code for certain targets. However, in this example the script just ignores the target name altogether.

The build generator creates an output file build_number.h that looks like this.

#ifndef BUILD_NUMBER_STR

#define BUILD_NUMBER_STR "20"

#endif

#ifndef BUILD_NUMBER

#define BUILD_NUMBER 20

#endif

#ifndef VERSION_STR

#define VERSION_STR "Ver 1.34 Build 20 - Thu Dec 16 09:38:08 MST 2010"

#endif

make_buildnum.sh Script Details

Each time the script runs, the BUILD_NUMBER and BUILD_NUMBER_STR will be incremented and VERSION_STR in the build_number.h file will be updated. A file called major_version contains the version number and will not be incremented by the tools. It will default to “0.0” but can be changed by editing the file. The file build_number contains the raw build number that the script increments. By default, it is created with a build number of 0. Editing this file will set the starting build number. The final result of the script is an updated build_number.h and build_number files.

One thing to note about this example is that every time make is called, the script is also called and the build_number gets incremented. If no source file has changed, the build number will still be incremented and force a recompile. From the GUI this could be a problem because every time you use “run as…” or “debug as…” the tool will try and recompile the project. There are two ways of fixing this; turn off the auto recompile feature in the GUI or add more intelligence in the make process ( discussed in the next section). To turn off the auto recompile feature of the of the GUI , select preferences in the Window menu. Then in the left hand pane choose launch from the Run/Debug item. Then unselect the Build (if required) before launching.

[pic]

If you compile your project from the command line you have a little more control of unwanted compiles so you may not want or need the extra complexity in the next section.

Source Sensitive Addition

Now we will add more complexity to this simple example to detect if any source files have changed before we blindly update the build number. Normally this is a simple. You call make with the –q option. If it returns 0 then everything is up to date and there is no need to update the build_number.h . However as described earlier, the Makefiles generated by the SBT always return a positive number even if there is nothing to build.

The workaround is to make/build the target twice. After the first build we will check to see if the .elf file was updated. If it is, we call our make_buildnum.sh script to update the build_number.h and then build again with the updated build_number.h file. Example 5 contains updated GNUmakefile.

#GNUmakefile in the application directory

# Example 5 build_number which only updates when source files change.

build_number: *.elf

sh make_buildnum.sh $@

%:

$(MAKE) -f Makefile $@

$(MAKE) build_number

@echo

@echo "********** BUILD" `cat build_number` "**********"

$(MAKE) -f Makefile $@

touch build_number

There are two targets in this GNUmakefile; one for build_number and one for everything else. When ”make all “ is called, make will use GNUmakfile and use the target “%:”. The first rule calls the normal make (Makefile). It recompiles all the files that are out of date and then updates the .efl file if needed. In the next rule, make is called with “build_number” as the target (notice no –f option so it will use the GNUmakefile). The build number depends on *.elf, so if the elf file is updated (newer than the build_number file) “make_buildnum.sh” will be run, incrementing the build_number .

The echo commands are purely cosmetic and just a visual indicator of the version number. Make is then called for the third time to pick up the build_number.h changes. The last rule “touch build_number” is needed to insure the build_ number file is newer than the .elf, and prevent any unnecessary updates to the build_number.

Conclusion

Make is a very powerful tool. By knowing a little about how make works, you can change its behavior or create interesting behaviors by using GNUmakefile and scripts, like the build_number generator described here.

Appendix A

make_buildnum.sh shell script

#!sh

# FILE: make_buildnum.sh

#echo what came in

echo Updating build number $1

# this script was drived from one posted on

#

# I had to change it so it would work with bash on the pc with out the bc command.

# version holds the major release number which does not get incremented.

# it is read from a file called major_version.

# If it doesn't exsist then is will be created with a default 0.0

if [ -e major_version ]

then version="`sed 's/^ *//' major_version`"

else version="0.0"

echo $version > major_version

fi

# buildnum holds the major release number which does get incremented.

# it is read from a file called build.number

# if it doesn't exsist then it is creatd with a default value of 0

if [ -e build_number ]

then buildnum="`sed 's/^ *//' build_number` "

else buildnum=0;

fi

#increment the buildnum by 1

buildnum=`expr $buildnum + 1`

#write it to a file

echo $buildnum > build.number.temp

#copy over the original

mv build.number.temp build.number

#print it to the screen

echo Build $buildnum

#versión..

#why - just because

echo "$version $buildnum - `date`" > version_number

#header

#now for the real work

echo "#ifndef BUILD_NUMBER_STR" > build_number.h

echo "#define BUILD_NUMBER_STR \"$buildnum\"" >> build_number.h

echo "#endif" >> build_number.h

echo >> build_number.h

echo "#ifndef BUILD_NUMBER" >> build_number.h

echo "#define BUILD_NUMBER $buildnum" >> build_number.h

echo "#endif" >> build_number.h

echo >> build_number.h

echo "#ifndef VERSION_STR" >> build_number.h

echo "#define VERSION_STR \"Ver $version Build $buildnum - `date`\"" >> build_number.h

echo "#endif" >> build_number.h

echo >> build_number.h

echo "#ifndef VERSION_STR_SHORT" >> build_number.h

echo "#define VERSION_STR_SHORT \"$version $buildnum\"" >> build_number.h

echo "#endif" >> build_number.h

echo >> build_number.h

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download