Thursday, December 13, 2007

Setting Environment Variables From Make Targets -- part II

I ran into a issue with the way that GNU make acts between cygwin and Solaris. In my previous post I used two instances of the same target, one to export the environment variable and one that executes the test. For example:

TST_CONFIG_MODULE := $(d)/test_config
...
check_config : export TST_DIR:= \
$(dir $(TST_CONFIG_MODULE) )
check_config : $(TST_CONFIG_MODULE)
$(TST_CONFIG_MODULE)


This is how it is documented in the GNU make manual and how it works under Cygwin. However, this fails on Solaris. The version of make installed on Solaris is only a couple of point releases different than the one on Cygwin so I wouldn't expect this to be an new feature. The immediate solution that I came up with is the same one that I used for the main check target.


check_config : $(TST_CONFIG_MODULE)
@for m in $(TST_CONFIG_MODULE) ; do \
export TST_DIR=`dirname $$m`; \
$$m;
done

Sunday, October 7, 2007

Setting Environment Variables From Make Targets

In my current project I needed to pass some pass information to some of my automated unit test programs. I needed the path that the program is being executed from to be able to read test data. As I am using a unit test framework I didn't have access to the command line variables and didn't want to take the time to change the framework to support this. So I decided to define an environment variable with the path information for the program. The unit tests can be run in two different ways, which needed different mechanisms to define the environment variable. The makefiles are setup with rules to run a single set of tests (e.g. make check_config), or running all unit tests (e.g. make check).

To support running a single set of unit tests the make file defines two targets. One that exports the environment variable and one that executes the test. For example:

TST_CONFIG_MODULE := $(d)/test_config
...
check_config : export TST_DIR:= \
$(dir $(TST_CONFIG_MODULE) )
check_config : $(TST_CONFIG_MODULE)
$(TST_CONFIG_MODULE)

Where the TST_CONFIG_MODULE defines the test program. The first check_config target exports an environment variable TST_DIR that is the directory component of, $(d), of TST_CONFIG_MODULE. The make file has to be structured this way as $(d) will not be valid by the time the target is evaluated.

For the second case the makefiles add test modules to the macro CHECK_MODULES and the check target just executes each element of this macro. As this is executed in a shell the target becomes:


check : $(CHECK_MODULES)
@for m in $(CHECK_MODULES) ; do \
export TST_DIR=`dirname $$m`; \
$$m;
done


It is a bit of a kludge to use the dirname executable to get the directory path from the test program name. Doing this assumes that we have dirname installed and that it is on the path. Bash has a built in to do this, ${dirname file}, but there does not seem to be such a builtin in sh.

Setting a Macro to Wildcarded Files

I found a mistake in one of my macro definitions. The macro was to hold all of the public header files and was defined as:

HEADERS := $(d)/*.h

This only set HEADERS to src/include/*.h instead of setting it to the files. To get they desired result the macro was changed to:

HEADERS := $(wildcard $(d)/*.h)

Saturday, August 4, 2007

Fortune Cookie Says

Hard words break no bones, fine words butter no parsnips.

Friday, July 6, 2007

Fortune Cookie Says

You will have a bright future.

Friday, June 8, 2007

Allowing Local Overrides in Make

I've been setting up a new project at work. I wanted to set up the makefile to allow a programmer to override settings without making changes to the makefiles. Make, unfortunately, does not provide a direct way to do this. I added a shell command to test for the existence of local file, and if found to return it. Make then tests the local file macro and includes the local rules file if defined. Adding the local rules file to svn ignore help to prevent any accidental changes to the makefiles. To illustrate here is the makefile.

## our directories
##
BASE_DIR := $(PWD)
RULES_DIR := rules
DOC_DIR := doc
SRC_DIR := src

## misc definitions
##
KERNEL := $(shell uname -s)
MACHINE := $(shell uname -m)
RULES_FILE := $(KERNEL)_$(MACHINE).mk
LOCAL_RULES := $(shell if [[ -e rules.local ]]; then echo rules.local; fi )

## load rules
##
dir := $(RULES_DIR)
include $(dir)/$(RULES_FILE)

# local overrides
ifdef LOCAL_RULES
include $(LOCAL_RULES)
endif

dir := $(DOC_DIR)
include $(dir)/Rules.mk

dir := $(SRC_DIR)
include $(dir)/Rules.mk

Friday, May 18, 2007

Fortune Cookie Says

A surprise treat awaits you.


Simplicity and clarity should be your theme in dress.

Sunday, April 22, 2007

Calendar Dimension

While working on a data mart and reporting system for a project I needed a way of pulling data for specific time periods for reports. This was done by using a calendar dimension, which is a table that specifies time frames primarily for reporting purposes. The dimension can be setup for any level of granularity that one needs. Since my smallest reporting period was a single month I setup a table like:
CREATE TABLE calendar_dim(
calendar_id NUMBER,
year NUMBER,
quarter NUMBER,
month_number NUMBER,
month_name VARCHAR2,
month_abbr VARCHAR2 )

The calendar_dim table is then populated with the calendar data that you need like:
1, 2006, 1, 1, 'January', 'JAN'
2, 2006, 1, 2, 'February', 'FEB'

Each table that contains dated information has a calendar_id column like:

CREATE TABLE foo_bar(
calendar_id NUMBER NOT NULL,
foo VARCHAR,
bar NUMBER )

The calendar dimension can then be used to control the time frame that queries are run for. As an example the following query sums the value bar over the first quarter of the year 2006.
SELECT fb.foo, SUM(fb.bar)
FROM foo_bar fb, calendar_dim c
WHERE
fb.calendar_id = c.calendar_id AND
c.year = 2006 AND
c.quarter = 1
GROUP BY
fb.foo;

Fortune Cookie Says

You have great physical powers and an iron constitution.

Sunday, April 15, 2007

Implementing Non-Recursive Make

I had to take over the web application piece of a project that I worked on after it became abandonware from the software vendor. I needed a build system for this project and decided to implement it using non-recursive make having previously read Peter Miller's paper. I based mine on the non-recursive make system presented by Emile van Bergen, but I ran into several problems with his approach. These are my notes on how I resolved my problems.

Documentation and Suggested Reading


Length Limit of Macros


The original presentation for implementing non-recursive make had individual macros defined to contain all of the source or object files. However, due to the sheer number of files in the webapp project, almost 2,000 after code generation, the upper bound for the length of a macro was exceeded. To resolve this problem my Makefile defines macros to hold the names of directory targets. Where each directory target is dependent upon the contents of per-directory macros, which contain the source or object files for that directory.

For example the Makefile contains:

...
CALLENTRY := $(DIR_LIBS)/fbCallEntry.jar
CALLENTRY_TGT :=
...
dir := com/fb/callentry
include $(dir)/Rules.mk
...

$(CALLENTRY) : $(COMMON) $(CALLENTRY_TGT)
@echo
...

The Rules.mk files in the callentry sub directories contain something like:

...
d := $(dir)
CALLENTRY_TGT += $(d)
...
CALLENTRY_SRCS_$(d) := \
$(d)/CallEntryObjectRegistry.java \
$(d)/CallHistoryItem.java \
$(d)/Customer.java

CALLENTRY_OBJ_$(d) := $(CALLENTRY_SRCS_$(d):%.java=%.class)
...

$(d) : $(CALLENTRY_OBJ_$(d))
$(d)/Rules.mk

CALLENTRY_OBJ_$(d) : CALLENTRY_SRCS_$(d)

Clean Targets


One of the goals was to keep the directory level details in the directory's Rules.mk file. However, this makes it difficult to implement directory level clean targets. The difficulty arises in that you no longer know the directory path after the rules.mk file has been loaded. The solution to this problem was to encode the directory path into the per-directory clean target, and then extract the directory path by using make's substitution rules.

For example the Makefile contains:

...
CLN_TGT :=
REAL_CLN_TGT :=

DIST_CLN_TGT:=
...

clean :$(CLN_TGT)

realclean : clean $(REAL_CLN_TGT)

distclean : realclean $(DIST_CLEAN_TGT)
...

The Rules.mk files in the callentry subdirectories contain something like:

...
CLN_TGT += CLN_$(d)
...

CLN_$(d) :
rm -f $(subst CLN_,,$@)/*.class
...

Tuesday, April 3, 2007

VIM

The VIM (VI iMproved) editor is available for most platforms and is available as a native Window's GUI application as the program gvim. The VIM editor has many customization features which are controlled by the vimrc configuration file.

VIMRC
The vimrc file may exist in several different locations and different names depending on your platform and installation. You can access vim's online help for additional information with the command :h vimrc. The default vimrc file does some nice things like setting up syntax highlighting so it is recommended that you either modify the default file or copy it for a user specific customizations. These are the settings that I use.

  • ai or autoindent This option automatically indents to the level of the previous line.
  • et or expandtab Insert an appropriate number of spaces instead of tabs.
  • sw or shiftwidth The number of spaces to use for each step of auto indent. Set this option to 4.
  • ts or tabstop The number of spaces a tab character counts for. Set this option to 4.
  • sts or softabstop The number of spaces a tab counts for while editing. Set this option to 4.
  • fdm or foldmethod This option enables code folding in the editor. I set this option to indent.

I set the last line of my vimrc file to:
set ai et sw=4 ts=4 sts=4 fdm=indent


Splitting Windows
  • :sp to split the current window into two views.
  • C-w C-w jumps between the windows.
  • C-w+ or C-w- to increase or decrease the view size.
  • C-wc or :q will close the selected view.

Using Tags
To enable tags do :set tag=.
C-] or C-W] follows tag for item under cursor (existing or split window).
C-T returns to your previous spot.
Searching tags :tag / or :tag for completion
:ts will list multiple matches.

Code Folding
The indent fold method works very well for source code if one uses good and consistent indentation. In this case vim will fold the code based upon the level of indentation. For example:


public void preInit(URL url) {
+--- 46 lines: or the default if no config files was specified.------- } // end preInit

public void init()
{
+--- 60 lines: preInit(getCodeBase());-------------------------------- } // end init

If one wants to use folding but not for source code then the fold method can be set to marker (fdm=marker). A marker will define a folding level for a hierarchy of
folds. A marker is the string { { { # where the # is a numeral defining the fold level. For example:

VIM {{{1
* To insert tabs when in expandtab (et) mode use C-V

windows {{{2 * :[#]sp to split the current window into two views


From command mode the following basic commands can be used to control the folding.
  • zR Open all folds
  • zo Open one fold under the cursor.
  • zO Open all folds under the cursor recursively.
  • zc Close one fold under the cursor.
  • zC Close all folds under the cursor recursively.

Misc Notes
  • To insert tabs when in expandtab (et) mode use {{C-V}}