The BENV build environment is a reusable make engine that greatly simplifies the process of setting-up and maintaining make files for projects. BENV effectively reduces a project’s makefile to two files: the first is a list of source code directories to compile and link. The second file is a list all of the project’s specific compile and link options/switches. That’s it. Couldn’t be simpler. Well, not exactly. The devil is in the details. The above description is correct once a specific host-compiler-target toolset has been configured. No matter what make system/engine you use, it must first be tailored/customized to your specific Host platform, compiler, linker, target OS and platform. What is different about BENV, is that this customization process is only done once. Once configured, a given toolset can be used on an unlimited number of projects.
BENV is built on top of GNU make. It was originally designed to run on Unix platforms and to support embedded, cross platform development. It has been successfully ported to run under Windows. The BENV engine is broken down into several subsystem or components. The structure is such that subsystems/components can be mixed and matched to meet the needs of a given toolset. Adding new toolsets is a straight forward process, but it does require general knowledge of MAKE, the compiler to be used, and the target platform. The following is very brief description of the major components of the BENV environment. A detailed reference guide is in progress, but currently not available.
|
Component |
Description |
|
host |
Contains the makefile fragments that define the computing environment in which BENV executes. |
|
targetOS |
Contains the of makefile fragments that describe the target OS for the compiler and linker. |
|
link |
Contains the makefile fragments that are used during the link phase of the build process. |
|
toolchain |
Contains the makefile fragments that specify the tools (compiler, assembler, linker, etc.) that are used during the build process. |
|
templates
|
Contains the top-level makefile fragments and shell script(s) for a given toolset. These files provide the ‘base make’ for new projects. |
|
make |
Contains the core makefile and make rules. It is independent of host/target/OS. |
|
bin |
Contains a collection of shell scripts that is used by the make engine. |
|
hostlinks |
Contains a collection of “links” to the host tools (compilers, linkers, etc.). This simplifies the process of identifying the absolute location of the host tools. |
|
latest |
Contains a minimum set of shell scripts that are stored at well-known, absolute location. The directory of this component is required to be in your command search path. The scripts contained in this component provide the minimum infrastructure needed to allow the other components of the build engine to be stored at an arbitrary root location. |
BENV is organized with certain assumptions about the directory structure of the make engine, the projects directories, and the source code. The following is a brief description of the top-level directory structure of the overall build/source code environment. The directory that contains the following directories is called the BENV root directory. The BENV root directory can be located anywhere.
|
Directory |
Description |
|
build |
Contains the majority of the files that make up the BENV build engine. |
|
hostlinks |
Contains the “links” to the various host tools (compilers, linkers, etc) that are integrated into BENV. |
|
iheaders |
This directory is used for the “indirect header include” build pattern. This build pattern allows code to include target/platform/compiler specific header files while at the same time being independent of any of the specifics. |
|
projects |
Contains all of the individual project directories. A project directory contains the top-level source code module (i.e. main()) and it is where the code is compiled (i.e. where you invoke make). |
|
src |
Contains 99% of the entire C/C++ source code base. All code under this directory is reusable by any/all projects. |
|
com,org,net,edu |
Contains the JAVA source code that is intended to be compiled under the BENV framework |
|
xsource |
Contains external, legacy, third-party C/C++ source code. Code in this directory has no absolute requirements placed on it - and is therefore only reusable at a very "coarse" level. |
|
xjava |
Contains external/third-party JAVA class files, jar files, beans, etc that can be “linked” to a JAVA project. No JAVA code is compiled from this directory. All third-party JAVA code to be compiled must be moved to the appropriate com/, org/, net/, edu/ directories. |
NOTE: Directories in italics are required for BENV to operate correctly.
NOTE: The following instructions assume that BENV had been previously installed and that the desired toolset already exists in BENV.
Each developer must modify his/her login profile with basic information about BENV.
Add the following the lines to your login profile. NOTE: it assumes that the ‘latest’ component is stored at /tools/latest/benv.
# Build Environment
export BENV_LATEST_ROOT=/tools/latest
export PATH=$PATH:$BENV_LATEST_ROOT/benv
For Unix system’s where GNU Make is not the native make, you must set an additional environment variable to inform BENV of the executable name of the GNU Make tool.
# Solaris
export BENV_GMAKE=gmake
Make the following modification to your environment. NOTE: it assumes that the ‘latest’ component is stored at c:\tools\latest\benv.
set BENV_LATEST_ROOT=c:\tools\latest
set PATH=%PATH%;%BENV_LATEST_ROOT%\benv
sources.b
main.cpp
libdirs.b
# This file contains a list of library pathnames
# relative to the build environment root directory.
#
# devtest
src/jcl/devtests/streamio/stdio
# streamio
src/jcl/streamio/stdio/api
src/jcl/streamio/stdio/win32
# support
src/jcl/strings/fixed
src/jcl/strings/common
src/jcl/strings/utils
src/jcl/errors/stderr/ansi
For JAVA example:
sources.b
JXCodeMain.java
libdirs.b
# This file contains a list of library pathnames
# relative to the build environment root directory.
#
#
com/shiftright/jcl/xcode
com/shiftright/jcl/asx
com/shiftright/jcl/cmdline
com/shiftright/jcl/types/numeric
# support
org/apache/avalon/excalibur/io
To build a project, simply set the current working directory to the desired project directory and type mk. Invoking mk with no options will build the “release” build of the project. To get a listing of available build options, type mk –h. The following sections provide additional details to the build process.
mk is a native shell script that is used to build the project. This additional layer provides the following functionality:
1. Provides a consistent “build interface” independent of the underlying tool(s) are actually used to build the project. This is important when projects are built using third-party and/or legacy components that are not easily ported to the BENV build environment.
2. Allows the build interface to be host platform independent.
buildCfg.h is a header file that the build engine automatically includes with every file that is compiled. This header file can be used to provide global macros, definitions, etc. The intent of this file is to facilitate debugging and as an alternative to specifying “compile” macros (i.e. replace the –Dmacro command line argument). Warning: Every c/cpp file is dependent on this file! This kind of a global dependency is usually a bad thing. It is recommended that you avoid using this file.
sources.b is makefile fragment file that is optionally used with every directory that is compiled. This file is used to specify which files in the directory are to be compiled. The file optionally exists in the source directory itself. If no sources.b file is specified, the build engine defaults to “compiling” all files in the directory. NOTE: Builds will run faster if you create a sources.b file for each directory.
flags.b is a makefile fragment file that can be used on a individual directory biases. This file allows additional compile options, or to override existing options when compiling a directory. This file optionally exists in the source directory itself. Typically this file is used for debugging purposes to selectively enable debug and/or tracing options for individual directories. This file should never be “permanently” used, i.e. never checked into the Revision Control System and never required for “release” builds.
ANSI projects are projects that have no host/target dependencies. These projects will compile and link using an ANSI compiler and libraries. Though the source code has no host dependencies – you still must specify a host environment to build the project. One way to do this would be to replicate the project for each host platform you would like to build it under. Another way would be to create a single project and at “build time” specify the host platform. BENV compromises by having a single project directory, but replicating the “make files” for each host platform. To build an ANSI project, the developer types mkxxxx, where xxxx is the desired host platform (i.e. mkwin32 or mkunix). An ANSI project is created simply by selecting it when running cfgmake. For example:
$ cfgmake
gnu/templates/java/unix/application/basic
gnu/templates/cross/gnu/ecos/powerpc-eabi/psim
gnu/templates/cross/gnu/legOS/h8300/user
gnu/templates/native/gnu/unix/mt
gnu/templates/native/gnu/unix/std
gnu/templates/native/gnu/unix/ansi
$ cfgmake
–c ansi
Initial Project Make files created.
$
JAVA projects are similar to ANSI projects in that they have no host/target run-time dependencies (only dependent on the host’s JVM). Though pure JAVA source code has no host dependencies – you still must specify a host environment to build the project. To build a JAVA project, the developer types mkxxxx, where xxxx is the desired host platform (i.e. mkwin32 or mkunix).
The file manifest.mf, if it exists, will be used as the manifest file when creating the jar file for the project. If the file does not exists, BENV will attempt to create a basic manifest file that specifies the ‘Main-Class’. BENV searches the project directory for a JAVA class that contains the “main()” method. If one and only one such class is found, a manifest is created using the founded class as ‘Main-Class’. If no match is found, then an empty manifest is created.
BENV was originally designed to run under Unix. BENV has been tested on Windows 2000 and ME and should be able to run on Windows 9x, NT, and XP. It has been ported to Windows by making use of Open Source ports of the standard Unix tools and shell. The main tools used are Cygwin and Zsh. Cygwin is used for the BASH shell emulation and the win32 port of GNU make. Zsh is used to provide native win32 implementations of the common Unix tools such as ls, grep, etc. By using Zsh instead of the complete Cygwin emulation, build time under Windows is greatly reduced.
What does all this mean? For Windows, the “latest” component contains the additional Cygwin/Zsh binaries that provide the Unix emulation. Also build times under Windows are slightly slower because of the emulation layer. There are no differences between Windows and Unix with respect to creating and building projects!
BENV started as build environment for C/C++ code, it has just recently been extended to build JAVA projects. The first significant build issue for JAVA projects is how to determine the file dependencies. Typically, this functionality is provided by the compiler. The JAVA compiler does not properly provide this functionality. As a result, a third party JAVA based tool, javadeps, is used to derive the file dependencies. The next issue is build times. First, the javadeps tool is a JAVA application which incurs a startup time penalty (as the JVM is initialized) each time it is invoked. Since it is called once per file, this adds up. Second, the JAVA compiler itself is slow to start-up and it too is called on a per-file basis. These start-up delays are serialized and add up to longer build times (compared to C/C++ projects). The slow build times are compounded on Windows boxes as BENV framework is slower to begin with.
In an effort to speed up build times, individual directories are compiled in a batch mode. If one file in a given directory needs to be rebuilt, the entire directory is rebuilt. This batch mode has the following characteristics. First, it has the cut the original build times in half. Second, the JAVA compiler and javadeps is called at most one time per directory. This effects the output of the build scripts by only echoing the status of first ‘dirty’ target detected.