Writing getLibs()

From RTSC-Pedia

Jump to: navigation, search
revision tip
—— LANDSCAPE orientation
[printable version]  [offline version]offline version generated on 30-Jul-2014 14:07 UTC

Writing getLibs()

Guidelines for package getLibs() functions

Contents

Introduction

Configuration is one of the phases of the RTSC flow. During configuration, a program configuration script loads a set of packages that contribute source code and libraries to the generated executable image. Packages may control which libraries they contribute by implementing getLibs() in their package.xs file. This document provides a guide to RTSC package producers for writing getLibs() for the most frequent use cases.

Configuration phase

The function getLibs() is used during RTSC configuration model’s generation phase. After the user’s configuration script finishes, and all loaded packages finish running their close() and validate() functions, RTSC configuration model invokes getLibs() for each loaded package.

{caption Figure 1}

Each package's getLibs() function returns libraries and/or object files, which are then forwarded to a linker command file template. The target-specific template transforms the list of libraries into a linker command file with the target-specific syntax. In the figure above, the linker command file contains the syntax specific to TI targets.

Program configuration parameters

The content of getLibs() consists of querying various configuration parameters of the global object Program, which represents properties of the executable being built. This module is globally available during RTSC configuration, and can be referred to simply as Program, but is also passed as the only argument to getLibs(). Based on Program's parameters, one or more of a package's libraries is supplied.

In most cases, a package selects libraries to supply based on:

  • which target is being used,
  • which CPU core is being used,
  • which device and platform are being used.

The following configuration properties of the module Program contain that information:

  • Program.build.target – an object representing RTSC build target, i.e. the target for which the executable is built
  • Program.cpu – an object representing the hardware for which the executable is built, including a specific device and CPU core
  • Program.platformName – the name of the platform

Target parameters

The recommended target parameter that package producers should use to identify the build target is suffix, which can be accessed during configuration phase as Program.build.target.suffix. The parameter suffix is a string that a target package producer assigns to its target as a unique readonly identifier.

Ideally, any other target that generates binary compatible code would have the same suffix, to advertise the compatibility with the original target. For example, two targets that use GCC compilers to generate code for Arm v5T architecture should have the same suffix.

Different GCC compilers configured for a same Arm architecture can still generate incompatible code, depending on the used compiler options. A producer of a RTSC target package must ensure that his target and an existing target are compatible, if they are going to use the same suffix. XDCtools do not have any additional mechanisms that test the compatibility between the targets.

Target compatibility

However, it is possible that independent producers select different suffixes for their compatible targets, and the package producers would have to be aware of compatibility of different suffixes to be able to supply the most compatible libraries. Also, if a new target backward compatible with an existing target is released after the package producer releases his package, the package may not be able to recognize that the libraries built for the older target can be supplied to builds for the new target.

In XDCtools 3.10, the object Program.build.target is enriched with a new target compatibility API. This API allows producers of RTSC target packages to declare compatibility between their targets and other existing RTSC targets. It also allows producers of RTSC packages to build their packages in such a way that the packages can be consumed in builds for targets that were not known at the time their packages were produced.

The API consists of a configuration parameter compatibleSuffixes and the function findSuffix(). The parameter compatibleSuffixes allows the target producers who created compatible targets with different suffixes to declare their targets compatible. Also, compatibleSuffixes allows target producers to declare one-way compatibility, where code produced by one target can be linked into an executable built by another target, but not the other way around.

In the case of two compatible targets mentioned above, each of them would have the other target's suffix in its own compatibleSuffixes array. In the case of one-way compatibility, a target that can accept other targets' code would list those targets' suffixes in its compatibilitySuffixes. For example, an Arm v7A target would have in its compatibleSuffixes array the suffixes for Arm v6 and Arm v5T targets. The suffixes are listed in order of preference, therefore the Arm v6 target should be listed before Arm v5T target.

Use cases

The function findSuffix() compares the list of libraries in a package with the build target's suffix and compatibleSuffixes, and returns suffix that is the best match between the two. The build target's suffix is the preferred match, so if the library package contains libraries for the build target, compatibleSuffixes array will not be considered. If the package does not contain any library compatible with the build target, the function returns null. Here is the sample code that selects a library based on the value returned from findSuffix().

package.xs
 
 
 
 
 
 
 
 
 
 
function getLibs(prog)
{
    var suffix = prog.build.target.findSuffix(this);
    if (suffix != null) {
        return ("lib/" + this.$name + ".a" + suffix);
    }
    else {
        return (null);
    }
}

Packages built with XDCtools 3.00 and other build systems

The function getLibs(), as written above will work only for packages built by XDCtools 3.05 and newer. Any package using an older XDCtools version or building libraries outside of the RTSC build cannot rely on the function findSuffix() to determine which libraries are available in the package. Such packages must explicitly supply a list of suffixes to findSuffix() and the function will return the suffix that is the preferred one by the build target. For example, if the package producer created libraries for targets with suffixes v5T, 470MV, 86U and 64P, the function getLibs() looks as follows:

package.xs
 
 
 
 
 
 
 
 
 
 
 
function getLibs(prog)
{
    var suffixes = ["v5T", "470MV", "86U", "64P"];
    var suffix = prog.build.target.findSuffix(suffixes);
    if (suffix != null) {
        return ("lib/" + this.$name + ".a" + suffix);
    }
    else {
        return (null);
    }
}

Packages loaded by XDCtools 3.05 and earlier

Both of the getLibs() examples above are written to be consumed only by XDCtools 3.10 or newer, because the code assumes that findSuffix() is always available. However, since that function was introduced only in XDCtools 3.10, if a package with any of the getLibs() versions above was loaded by an older version of XDCtools, the error TypeError: Cannot find function findSuffix will be displayed. The following version of getLibs() queries Program.build.target for findSuffix and if it is not found, getLibs() reverts to simply using Program.build.target.suffix to look for compatible libraries.

package.xs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function getLibs(prog)
{
    if ("findSuffix" in  prog.build.target) {
        var suffix = prog.build.target.findSuffix(this);
        if (suffix != null) {
            return ("lib/" + this.$name + ".a" + suffix);
        }
    }
    else {
        var lib = "lib/" + this.$name + ".a" + prog.build.target.suffix;
        if (java.io.File(this.packageBase + lib).exists()) {
            return(lib);
        }
    }
    return (null);
}

Supplying libraries based on profiles

Another common case is a package with multiple libraries built for the same target, but one of them built in the debug mode and another one in the release mode. In that case, besides figuring out the right suffix, getLibs() should check the property profile of its own package. That property can be accessed through this.profile in getLibs(). In all functions in package.xs, this refers to the current package.

package.xs
 
 
 
 
 
 
 
 
 
 
 
 
function getLibs(prog)
{
    var suffix = …;
 
    var lib = "lib/" + this.profile + "/" + this.$name + ".a" + suffix;
    if (java.io.File(this.packageBase + lib).exists()) {
        return(lib);
    }
    else {
        return("lib/release/" + this.$name + ".a" + suffix);
    }
}

The first part of the script, ommitted for brevity, selects a compatible suffix or returns null if there are no suffixes compatible with the build target. Then, the function builds the library name using this.profile. The property this.profile of the current package could be explicitly set in the program configuration script, or if it is not set at all for this package, then it will be set to Program.build.profile property. In any case, the function getLibs() does not change. However, the use case where this.profile can be set to any string allows the package producer to document custom profiles in the package documentation, and then to deliver libraries corresponding to those profiles. For example, a package producer can have two additional libraries in optimize_speed and optimize_space modes for suffixes v5T and 470MV, in addition to common debug and release available for all targets that the package supports. The function getLibs() would not change in that case, but it would still deliver libraries built for custom modes, or release libraries for targets that do not support those custom modes.

Supplying libraries based on device

The mechanism of offering different profiles can allow package producers to build many custom libraries, and to document in which cases such libraries can be used. However, package consumers may want to leave it to the producers to determine the most appropriate library based on different package properties. A package producer may decide to return different libraries for different devices that all build for the same target. For example, the DSP-side of OMAP2530 and OMAP3530 devices may be best served by different libraries, even though they both can run code built for the ti.targets.C64P target with the suffix 64P.

package.xs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function getLibs(prog)
{
    var suffix = …;
 
    switch (suffix) {
        case "64P":
            if (prog.cpu.deviceName == "OMAP2530") {
                return ("lib/" + this.$name + "_2530.a64P");
            }
            else if (prog.cpu.deviceName == "OMAP3530") {
                return ("lib/" + this.$name + "_3530.a64P");
            }
            else {
                return ("lib/" + this.$name + ".a64P");
            }
            break;
        case default:
            return ("lib/release/" + this.$name + ".a" + suffix);
            break;
    }
}

Please note here that even though we are dealing with devices that have two cores, where the parameter deviceName does not specify if we are building for DSP or GPP core, we did not check which of these cores will be used to run the executable. We relied on the program configuration script or the platform package to ensure that the C64P target is matched with the right hardware platform. Platforms are better equipped to catch such an issue, rather than many different library packages verifying the right match between the target and the platform. But, in the case the package producer needs to check the hardware platform for the OMAP2530 device, for example, there is a property Program.cpu.attrs.cpuCore, which will contain v6 if the program is being built for the Arm core or C64x+ if the program is built for the DSP core.

Don't use platformName to determine the device, for example by searching the platform name for a specific device name. A user can use the same device with different platforms, where platformName may or may not contain the name of a device. For example, a user can use the device OMAP2530 with 'ti.platforms.generic', instead of using evm2530 platform.

Packages with multiple releases

This section relies on undocumented features that are supported in XDCtools 3.10 and earlier releases, but may not be supported later and could be replaced with a different interface for querying the current package release.

Different releases of the same library package may contain only a subset of libraries built in the package. For example, a release delivered to a subset of customers may contain a debug mode version of the supplied libraries, while a release delivered to another subset of customers may contain libraries optimized for speed or memory footprint, but not a debug version of the libraries. In some simple cases, code in getLibs() can simply check the existence of the libraries using java.io.File.exists() interface. However, in more complex cases it must be necessary to know the actual release.

Releases can be identified by the required parameter name. The parameters of a release are available in the XML file package.rel.xml in the directory package of the released package. The parameter name is the string supplied as the name of the release, when calling Pkg.addRelease(), in package.bld.

package.xs
 
 
 
 
 
 
 
 
 
 
 
 
 
function getLibs(prog)
{
    var release = xdc.loadXML(this.packageBase + "/package/package.rel.xml");
    var releaseName = release.@name;
 
    if (releaseName == "pkgs_myPkg,full") {
        ...
    }
    else if (releaseName == "pkgs_myPkg,withDebug") {
        ...
    }
    ...
}

Alternatively, the optional parameter label from Release.Attrs can be used.

package.xs
 
 
 
 
 
 
 
 
 
 
 
 
 
function getLibs(prog)
{
    var release = xdc.loadXML(this.packageBase + "/package/package.rel.xml");
    var releaseLabel = release.@label;
 
    if (releaseLabel == "default") {
        ...
    }
    else if (releaseLabel == "withDebug") {
        ...
    }
    ...
}

Changing compatibleSuffixes

A package consumer configuring the application can determine that compatibleSuffixes array for his build target is incomplete, i.e. there are some new targets available that the target package producer did not include in compatibleSuffixes. In his config.bld, the package consumer can change compatibleSuffixes configuration parameter.

For example, a user could be building an application for the gnu.targets.arm.GCArmv6 target, whose compatibleSuffixes is set to ["v5T", "470MV"]. If there is a new GCArmv4 target, which generates code compatible with code for GCArmv6, the user can add GCArmv4.suffix to GCArmv6.compatibleSuffixes.

config.bld
 
 
 
var armTargets = xdc.loadPackage("gnu.targets.arm");
armTargets.GCArmv6.compatibleSuffixes = ["v5T", "470MV", "v4"];
...
Personal tools
package reference