From RTSC-Pedia

Jump to: navigation, search
revision tip
—— LANDSCAPE orientation
[printable version]  offline version generated on 04-Aug-2010 21:08 UTC  

RTSC Packaging Primer/Lesson 5

Package Build — compiling charlie.sqrtlib libraries

Continuing to examine packages from their producer's perspective, charlie.sqrtlib in many ways represents one of the "sweet spots" of RTSC—a standardized vehicle for delivering and deploying C headers and libraries, ultimately consumed within the sorts of client applications presented in Lesson 3. Unlike charlie.sqrtlib.samples (which, if you recall from the last lesson, housed only a handful of files delivered "as is"), producing a package such as charlie.sqrtlib requires us to first address the mechanics of package build—prescribing the compilation of a common base of C source code into multiple function libraries for multiple RTSC targets.

We'll in fact continue to examine charlie.sqrtlib over the next few lessons, exploring different dimensions of RTSC packaging from the producer's perspective. In several cases, we'll further refine the package.xdc and package.bld files first introduced in this lesson.

Contents

Specifying the package

The XDCspec package.xdc file for charlie.sqrtlib follows the same pattern seen first in Lesson 4, but now adds an explicit declaration of its Settings meta-module within the scope of this package.

charlie/sqrtlib/package.xdc
 
 
1
 
/*! Library of square-root functions */
package charlie.sqrtlib {
    module Settings;
}

The declaration at line 1 merely introduces the Settings module; Lesson 7 will examine a separate XDCspec source file that further specifies client-visible features at the module level—such as the Settings.optimize configuration parameter used back in Lesson 3.

As with any programmatic scope—whether fields within a struct or local variables within a function—all modules must have unique names within the domain of their containing package. But since every RTSC package would already have a globally-unique name, the Settings module declared at line 1 likewise acquires its own (globally-unique) canonical identifier—charlie.sqrtlib.Settings in this case—which clearly distinguishes this particular module from any others with the same name, but in different packages.

More on package names.  Recalling our use of multi-segment paths within #include directives from Lesson 3—the standard C idiom for identifying header files housed inside RTSC packages, such as charlie/sqrtlib/isqrt.h—we can't emphasize enough the importance of each package having a globally-unique name. Just as other RTSC packages can (and already do!) define their own Settings module, the supplier of charlie.sqrtlib also need not worry about potential clashes at the client's site with identically-named header files delivered in differently-named packages.

Mapping globally-unique package names to corresponding package directory paths simply reinforces this scheme, since (by definition) the charlie/sqrtlib directory can contain no more than one file named isqrt.h; searching the ordered set of repositories comprising the current package path—each of which (by definition) can have no more than one directory named charlie/sqrtlib beneath them—simply completes the story.

But with one critically important caveat:  RTSC package names should only appear in lowercase, and not UPPERCASE or even camelCase. Following this naming convention—borrowed from Java, of course—not only allows us to easily spot RTSC packages in context, but also dances gracefully around the case-insensitivity of the Windows file-system.

So please, no packages named charlie.sqrtLib; and likewise, no packages with a pair of files named isqrt.h and ISQRT.h.

In the final analysis, though, the real challenge becomes choosing meaningful and stable programmatic identifiers for each RTSC package—no less important than the intellectual effort we already spend as C programmers naming our public types and functions. Said in other words, renaming a RTSC package once released has the same repercussions as renaming symbolic constants in headers or altering function signatures in libraries—each of these acts breaks existing programmatic contracts between suppliers and their clients, with untold impact to legacy applications.

While each RTSC package name canonically maps to a directory name, don't conversely presume that existing directory structures used in your current development flow provide a ready-made set of meaningful and stable RTSC package names. In practice, these directory trees often reflect transient organizational traits—a project code-name, the team's location, current status of the software—rather than persistent structural or functional qualities of the content itself; RTSC package names should try to emphasize the latter. Worse, these hierarchies can become excessively deep—again, often reflecting organizational complexities rather than architectural richness; exercise restraint here, since the RTSC package names you choose eventually become programmatic identifiers in someone else's source code.

Preparing config.bld

Before moving on to the package.bld script for charlie.sqrtlib, you'll need to prepare a special source file named config.bld which plays an important role within the overall RTSC build flow. Also written in XDCscript, config.bld provides a centralized locale for encapsulating the setup of individual RTSC targets and platforms—often involving information very specific to your development environment. Since execution of config.bld always occurs at the very beginning of the RTSC build flow, individual package.bld scripts can in general remain insulated from the specific palette of targets and platforms currently in play.

In practice, one config.bld script will typically define a common set of targets and platforms used consistently across an entire family of packages during production. To facilitate sharing, simply place your config.bld script at the root of some active package repository; the XDCtools in fact locate the current config.bld file by searching the current package path.

Since our package path now consists of just the «examples» directory, you'll find the current script in «examples»/config.bld.

config.bld

 
2a
2b
 
3a
 
 
3b
 

var Build = xdc.useModule('xdc.bld.BuildEnvironment');
 
var C64P = xdc.useModule('ti.targets.C64P');
var GCC = xdc.useModule('gnu.targets.Mingw');
 
C64P.rootDir = "«c6xtools»";
C64P.platform = 'ti.platforms.sim64Pxx';
 
GCC.rootDir = "«gcctools»";
 
Build.targets = [C64P, GCC];

Working backwards, the assignment at line 4 defines the array of RTSC targets actually used when we'll build the charlie.sqrtlib package later on; Build refers here to the special BuildEnvironment meta-module imported at line 1 which, as we'll see shortly, works hand-in-hand with the PackageContents module first introduced in Lesson 4. The Build.targets array itself consists of XDCscript objects corresponding to other distinctly-named RTSC modules first imported at lines 2a and 2b.

In the final analysis, setting up package build targets boils down to configuring modules like ti.targets.C64P—just as we've already configured the Bench and Settings modules back in Lesson 3, or how we've already assigned PackageContents parameters in Lesson 4. Like prog.cfg and package.bld, config.bld clearly "does something".

If you're "playing along", alter the strings on lines 3a and 3b to respectively reference the installation directories for the C6000 and GCC tools chosen back in Lesson 0. Linux-hosted developers should also substitute the 'gnu.targets.Linux86' module on the line 2b.

Prescribing "what" to build

Similar to what you've already seen in Lesson 4, the package.bld script for charlie.sqrtlib will programmatically define "what" to produce—for now, prescribing that this particular package will build a pair of function libraries named isqrt_loop and isqrt_unroll for each RTSC target.

charlie/sqrtlib/package.bld
 
 
 
 
 
1
2
 
 
 
 
 
var Build = xdc.useModule('xdc.bld.BuildEnvironment');
var Pkg = xdc.useModule('xdc.bld.PackageContents');
 
/* ---- what to build ---- */
 
for each (var targ in Build.targets) {
    Pkg.addLibrary("lib/isqrt_loop", targ).addObjects(["isqrt_loop.c"]);
    Pkg.addLibrary("lib/isqrt_unroll", targ).addObjects(["isqrt_unroll.c"]);
}
 
/* ---- what to release ---- */
    ...

With config.bld fresh in your mind—and in fact executed ahead of entering package.bld—the reference to the Build.targets array here on line 1 indeed tracks its earlier assignment back at line 4 of the config.bld script found along the current package path. In this particular setting, we've bound the loop variable targ to each successive element of the Build.targets array using a standard JavaScript for each statement—a very expressive XDCscript programming idiom which we'll use whenever iterating over a set of values.

Inside this for loop, the addLibrary calls at line 2 fully characterize the pair of C libraries compiled later on for each pre-configured target. In its simplest form, the addLibrary function inputs the prospective library's generic name (given as a relative path with respect to the current package directory, minus any target-specific file extensions) plus a specific RTSC target (bound here to the local variable targ). The newly-created XDCscript object returned by addLibrary further supports an addObjects function whose input consists of an array of source-file names.

The RTSC build flow in general and the xdc.bld.Library meta-module in particular support a wealth of more advanced features that give package producers complete control of the underlying compilation steps—adding -I and -D options to the command-line, selecting pre-defined compilation profiles such as "debug" or "release", or even overriding standard file extensions such as .a64P. Where appropriate, these attributes can apply to individual files within the library or else to the library as a whole. Package producers can furthermore override built-in defaults more globally, either by assigning corresponding attributes of the PackageContents module within individual package.bld files or else by configuring RTSC targets such as ti.targets.C64P within a more centralized config.bld script.

Portable Sources.  Though it might seem a bit off-topic, let's take a quick look at some C source code—specifically, the space-efficient variant of the isqrt function declared within isqrt.h and implemented within isqrt_loop.c.

charlie/sqrtlib/isqrt.h
 
extern unsigned int isqrt( unsigned long val );

charlie/sqrtlib/isqrt_loop.c
 
 
 
 
 
 
 
 
 
 
 
 
 
unsigned int isqrt( unsigned long val )
{
    unsigned long temp, guess = 0, mask = 0x8000, shift = 15;
 
    do {
        if (val >= (temp = (((estimate << 1) + mask) << shift--))) {
           estimate += mask;
           val -= temp;
        }
    } while (mask >>= 1);
 
    return guess;
}

Those of you with a background in numerical analysis will recognize the Newton-Raphson method behind this rather compact piece of code. In the interest of space [sic!], we'll skip the rather lengthy time-efficient variant implemented within isqrt_unroll.c. You'll find more information on these (and many other) implementations of square-root in C here.

Needless to say, isqrt_loop.c is portable C source code—or at least "portable enough" that we can confidently compile the same source file using different RTSC targets corresponding to different C compilers generating object code instructions for different processor architectures. As C programmers, portability always remains an admirable and desirable objective whenever producing target content.

Our package.bld script has in fact achieved a similar level of portability—focusing on "what" to build rather than the details of "how" to build for a particular target. To really appreciate what we've achieved here, imagine maintaining a makefile (like the one found in charlie/sqrtlib/samples) which builds code using two (or three, or N) different tool-chains—all while hosted on Windows and Linux!!

Indeed, both the generality and scalability of our package.bld script over (say) a more declarative makefile results from something that lies at the very core of programming itself—the for loop back at line 1, which allows us to formulate a closed expression of the problem at hand.

We don't expect to change isqrt_loop.c for each new target.... So why can't package.bld remain portable as well?

Using the xdc command

Courtesy of the xdc command introduced in Lesson 4, we'll now clean-and-rebuild charlie.sqrtlib by invoking xdc clean followed by xdc all from within the package directory. Output from the latter command (edited slightly to improve readability) traces all the steps involved in transforming charlie.sqrtlib into a built package.

 

 

 
3a
3b
3c
 
 
 
4c
 
 
 
5c
 
 
 
6c
 
 
%> xdc all
making package.mak (because of package.bld) ...
generating interfaces for package charlie.sqrtlib (because ...) ...
    translating Settings
 
cl64P isqrt_loop.c ...
cl64P package/package_charlie.sqrtlib.c ...
archiving package/lib/.../isqrt_loop.o64P ... into lib/isqrt_loop.a64P ...
 
cl86GW isqrt_loop.c ...
cl86GW package/package_charlie.sqrtlib.c ...
archiving package/lib/.../isqrt_loop.o86GW ... into lib/isqrt_loop.a86GW ...
 
cl64P isqrt_unroll.c ...
cl64P package/package_charlie.sqrtlib.c ...
archiving package/lib/.../isqrt_unroll.o64P ... into lib/isqrt_unroll.a64P ...
 
cl86GW isqrt_unroll.c ...
cl86GW package/package_charlie.sqrtlib.c ...
archiving package/lib/.../isqrt_unroll.o86GW ... into lib/isqrt_unroll.a86GW ...
 
all files complete.

No different from when we built charlie.sqrtlib.samples in Lesson 4, the output at line 1 confirms generation of package.mak based upon the outcome of executing package.bld (which further leverages the results of first running the config.bld script found along the package path). In light of our earlier comments about the portability of package.bld versus a makefile, think of the xdc command as akin to a high-level language compiler—essentially transforming package.bld source code into package.mak object code that ultimately does the "real work".

When yesterday's source code becomes tomorrow's object code, that's progress.... Just as C compilers mechanically generate elaborate assembly language sequences that no programmer would necessarily craft by hand nor wish to maintain over time, the (often lengthy!) package.mak file generated by the xdc command after first running your package.bld likewise leverages many advanced features of the underlying gmake tool. Besides accurately capturing a wealth of inter-file dependencies that minimize re-build—a tedious process without automated support—the generated package.mak file also can robustly support parallel builds on multi-processor hosts by invoking the command xdc --jobs=N  .... In general, the xdc command will simply forward any gmake options its receives to the underlying tool.

Moving on, command output at line 2 reflects some processing of a separate XDCspec source file associated with the Settings module introduced earlier at line 1 of the package.xdc for charlie.sqrtlib; more about this in Lesson 7. The remaining output delineates compilation of (portable) C source files into function libraries, yielding one pair of libraries for each RTSC target configured at line 4 of config.bld.

Using the isqrt_loop.a64P library as an exemplar for the others, the output at line 3a first confirms compilation of one of this package's source files (isqrt_loop.c) into a corresponding object file (isqrt.o64) buried (far!) beneath the special package sub-directory found in all built packages. Creating the library itself at line 3c will then pick up this previously generated object file—along with any others slated for inclusion in this particular library.

Rationalizing the generated package/package_charlie_sqrtlib.c source file compiled here at line 3b would take us far off-course from the topic at hand. Suffice it to say that the resulting .o64 object file—likewise archived into isqrt_loop.a64 at line 3c—has no effect upon client applications linking against this library further downstream.

Finally, we should once again emphasize that the xdc command will not only generate package.mak when needed, but will also forward this synthesized file to gmake which in turn dispatches the individual steps actually required to build the package. As you'd expect from gmake, invoking xdc all after modifying (say) isqrt_loop.c would only trigger a re-build of the libraries identified at lines 3c and 5c; but modifying (say) the common isqrt.h header included by each .c source file would trigger a re-build of all libraries. Needless to say, changes further upstream to package.bld (adding another library) or config.bld (adding another target) would force re-generation of package.mak itself.

See also

Managing Compiler Toolchains How to manage XDCtools use of your compiler toolchain
xdc.bld.BuildEnvironment Client documentation for xdc.bld.BuildEnvironment

[printable version]  offline version generated on 04-Aug-2010 21:08 UTC  
Copyright © 2008 The Eclipse Foundation. All Rights Reserved
Views
Personal tools
package reference