All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Component Definition .cdef

Component.cdef files can contain these sections:

Bundles

Lists additional files or directories to be copied from the build host into the app so they’re available to the app at runtime (e.g., audio files, web pages, executable scripts or programs built using some external build system).

bundles:
{
    file:
    {
        // Include the web server executable (built using some other build tool) in the app's /bin.
        [x] 3rdParty/webServer/bin/wwwServ  /bin/

        // Put the company logo into the app's /var/www/ for read-only access by the web server.
        images/abcCorpLogo.jpg  /var/www/

        // Make the appropriate welcome page for the product appear at /var/www/index.html.
        webContent/$PRODUCT_ID/welcome.html  /var/www/index.html

        // Create a file to record persistent custom audio messages into.
        [w] audio/defaultMessage.wav  /usr/share/sounds/customMessage.wav
    }

    dir:
    {
        // Recursively bundle the directory containing all the audio files into the app.
        // It will appear to the app read-only under /usr/share/sounds/.
        audio   /usr/share/sounds
    }
}

Three things need to be specified for each file or directory:

  • access permissions
  • build system path
  • target path

Access permissions - any combination of one or more of the following letters, enclosed in square brackets:

  • r = readable
  • w = writeable
  • x = executable

If permissions values are not specified, then read-only ([r]) is the default.

Directories always have executable permission set so they can be traversed. Setting the [x] permission in the dir: subsection causes the files under the directory to be made executable.

Setting [w] in the dir: subsection causes all files under that directory to be writeable, but the directory itself will not be writeable.

Note
Directories in the persistent (flash) file system are never made writeable because the on-target flash file system does not support usage quotas (yet).

Build system path - file system path on the build PC where the file is located at build time.

The path can be relative to the directory where the .adef file is located.

Note
Environment variables can be used inside these paths.

Target path - file system path on the target where the file will appear at runtime.

It's an absolute path inside the app's sandbox file system.

If the path ends with '/', it means the directory path where the source object (file or directory) will be copied. The destination object will have the same name as the source object.

If the path doesn't end in a '/', it's a full destination object path. The destination object could have a different name than the source object.

Note
If the app is running unsandboxed, the bundled files and directories can be found in their installation location under /opt/legato/apps/xxxx, where xxxx is replaced by the app name.

Quoting Paths

File paths can be enclosed in quotation marks (either single ' or double "). This is required when the file path contains spaces or comment start sequences

"//" or  "/*"

File Ownership and Set-UID Bits

When the app is installed on a target:

  • the owner and group are set to root on all files in the app.
  • the setuid bit is cleared on everything in the app.

C Flags

Provides a way to specify command-line arguments to pass to the compiler when compiling C source code files.

Flags are separated by whitespace.

cflags:
{
-g -O0
-DDEBUG=1
}

C++ Flags

Provides a way to specify command-line arguments to pass to the compiler when compiling C++ source code files.

Flags are separated by whitespace.

cxxflags:
{
-std=c++0x
-g -O0
}

Linker Flags

Provides a way to specify command-line arguments to pass to the compiler when linking C/C++ object (.o) files together into a component shared library (.so) file.

Flags are separated by whitespace.

ldflags:
{
-Lfoo/bar
}

Pools

Warning
This feature not yet implemented.

Specifies the number of memory pool blocks that each memory pool should contain.

pools:
{
myPool = 45
}

Provides

Lists things this component provides (exports) to other software either inside or outside of the app.

The only subsection supported today is the api subsection.

API

Lists IPC services provided by this component to other components.

Contents use the same syntax as the requires: API section, except the options are different.

Here's a code sample where greet.api defines a function called Send() where the C source code for the component (in greetServer.c) is implement a function called greet_Send().

provides:
{
api:
{
greet.api // We offer the Greet API to others so they can say “hello” to the world.
heat = digitalOutput.api
cool = digitalOutput.api
}
}
sources:
{
greetServer.c
tempControl.c
}

The component must implement the API functions being provided.

In C, the source code must #include “interfaces.h” to get the auto-generated function prototype definitions and type definitions. The function and type names defined in the .api files are prefixed with the interface name and an underscore (similar to required APIs).

[manual-start] Option

To reduce the initialization code a component writer needs to write, the build tools automatically try to advertise the service when the executable is run. Sometimes this is not the preferred behaviour.

The [manual-start] option tells the build tools not to automatically advertise this API with the Service Directory when the process starts. If [manual-start] option is used, the component can control when it wants to start offering the service to others by calling the xxxx_AdvertiseService() function explicitly in the component source code when it's ready.

provides:
{
api:
{
foo.api [manual-start]
}
}

[async] Option

The server of a service can also implement the functions as if they were called directly by the client (even though the client may be running inside another process). When the client calls an API function, the server's API function gets called, and when the server returns from the function, the function returns in the client process.

Sometimes the server needs to hold onto the client request and do other things (like handing requests from other clients in the meantime) before sending a response back. This is called asynchronous mode, and is enabled using the [async] keyword on the end of the api section entry:

provides:
{
api:
{
bar.api [async]
}
}

When asynchronous mode is enabled for a server-side interface, the generated code changes as follows:

  • commandRef parameter is added to the beginning of all the API functions' parameter lists.
  • return value is removed from every API function.
  • Respond() function is generated for every API function.

In async mode, the server responds to the client's call to API function F() by calling the associated FRespond() function.

The Respond functions all take the commandRef as their first parameter. If an API function has a return value, that return value is sent to the client through the second parameter of the Respond function. Any output parameters defined in the API function are also passed as parameters to the Respond function.

See Language-independent APIs for more information, or try it and have a look at the generated header files.

Requires

The requires: section specifies things the component needs from its runtime environment.

It can contain various subsections.

API

Lists IPC APIs used by this component.

Here's a code sample of a component using the Configuration Data API (defined in le_cfg.api) to read its configuration data:

requires:
{
api:
{
le_cfg.api
}
}

This creates a client-side IPC interface called le_cfg on this component, and it makes the functions and data types defined inside le_cfg.api available for use in the component's program code.

The name of the .api file (minus the .api extension) is the name of the interface, and in C code, the names of functions and data types defined in the .api file are prefixed with the name of the interface with an underscore separator.

requires:
{
api:
{
print.api // WriteLine() from the API will appear in my C code as "print_WriteLine()".
}
}

To rename the interface, an interface name followed by an equals sign ('=') can be added in front of the .api file path.

requires:
{
api:
{
hello = greet.api // Send() from the API will appear as "hello_Send()" in my code.
}
}

Multiple instances of the same API listed in the api: section must have unique instance names, and appear as separate functions with different prefixes.

requires:
{
api:
{
heat = digitalOutput.api // Used to turn on and off the heater.
cool = digitalOutput.api // Used to turn on and off the cooling (A/C).
}
}

If digitalOutput.api defines two functions On() and Off(), the component’s source code would have four functions available to it: heat_On(), heat_Off(), cool_On(), and cool_Off().

C/C++ source code must #include “interfaces.h” to use the auto-generated function definitions. The build tools will automatically generate a version of interfaces.h customized for your component that includes all declarations for all the interfaces the component uses.

The build tools search for the interface definition (.api) file based on the interface search path.

Options

To reduce the amount of initialization code a component needs to write, the build tools automatically generate the client-side IPC code for that API, and automatically try to connect to the server when the executable is run. There are a couple of options that can be used to suppress this behaviour.

The [types-only] option tells the build tools the client only wants to use type definitions from the API. This means the client-side IPC code will not be generated for this API, but the types defined in the API will still be available to the component (through interfaces.h in C/C++).

The [manual-start] option tells the build tools not to automatically connect to this API's server when the process starts. This means the component can control when it wants to connect to the server by calling the xxxx_ConnectService() function explicitly in the component source code.

requires:
{
api:
{
foo.api [types-only] // Only need typedefs from here. Don't need IPC code generated.
bar.api [manual-start] // I'll start this when I'm ready by calling bar_ConnectService().
}
}

File

Specifies:

  • files residing on target, outside of app, but made accessible to app.
  • locations inside app's sandbox where files will appear.

Things listed in requires are expected to be found on the target at runtime. They're not copied into the app at build time; they are made accessible to the app inside of its sandbox at runtime.

Each entry consists of two file system paths:

  • path to the object in the file system outside of the app, which must be an absolute path (beginning with ‘/’).
  • absolute file system path inside the app’s sandbox where the object will appear at runtime.

A file path can be enclosed in quotation marks (either single ' or double "). This is required when it contains spaces or character sequences that would start comments.

The first path can't end in a '/'.

If the second path ends in a '/', then it's specifying the directory where the object appears, and the object has the same name inside the sandbox as it has outside the sandbox.

requires:
{
    file:
    {
        // I get character stream input from outside via a named pipe (read-only)
        /var/run/someNamedPipe  /var/run/

        // I need read and write access to UART2.
        /dev/ttyS1  /dev/

        // I need to be able to play back audio files installed in /usr/local/share/audio.
        "/usr/local/share/audio/error message.wav" /usr/share/audio/
        '/usr/local/share/audio/success message.wav' /usr/share/audio/
    }
}
Note
Even though the file system object appears in the app's sandbox it still needs permissions settings on the file. File permissions (both DAC and MAC) and ownership (group and user) on the original file in the target system remain in effect inside the sandbox.

It's also possible to give the object a different names inside and outside of the sandbox by adding a name to the end of the second path.

requires:
{
    file:
    {
        /dev/ttyS0  /dev/port1     // Program uses /dev/port1, but UART0 is called /dev/ttyS0.
    }
}
Warning
When something is accessible from inside an app sandbox, there are potential security risks (e.g., access to the object could be exploited by the app, or hacker, to access sensitive information or launch a denial-of-service attack on other apps within the target device or other devices connected to the target device).

Dir

Specifies directories on target device to make accessible to the app.

The location inside the app's sandbox at which the directory will appear is also specified.

Things listed here are expected to be found on the target at runtime. They are not copied into the app at build time; they are made accessible to the app inside of its sandbox at runtime.

Each entry consists of two file system paths:

  • The first path is the path to the directory outside of the app. This must be an absolute path (beginning with ‘/’) and can never end in a '/'.
  • The second path is the absolute path inside the app’s sandbox where the directory will appear at runtime.

Paths can be enclosed in quotation marks (either single ' or double "). This is required when it contains spaces or character sequences that would start comments.

If the second path ends in a '/', then it's specifying the directory into which the object will appear, and the object will have the same name inside the sandbox as it has outside the sandbox.

requires:
{
    dir:
    {
        // I need access to /proc for debugging.
        /proc   /

        // For now, I want access to all executables and libraries in /bin and /lib.
        // Later I'll remove this and replace with just the files I really need in the field.
        // Also, I don't want to hide the stuff that the tools automatically bundle into my app's
        // /bin and /lib for me, so I'll make the root file system's /bin and /lib accessible as
        // my app's /usr/bin and /usr/lib.
        /bin    /usr/bin
        /lib    /usr/lib
    }
}
Note
Even though the directory appears in the app's sandbox doesn't mean the app can access it. The directory permissions settings must also allow it. File permissions (both DAC and MAC) and ownership (group and user) on the original files in the target system remain in effect inside the sandbox.
Warning
Any time anything is made accessible from inside an app sandbox, the security risks must be considered carefully. Ask yourself if access to the object can be exploited by the app (or a hacker who has broken into the app) to access sensitive information or launch a denial-of-service attack on other apps within the target device or other devices connected to the target device?
Note
It's not possible to put anything inside of a directory that was mapped into the app from outside of the sandbox. If you require /bin to appear at /usr/bin, you can't then bundle a file into /usr/bin or require something to appear in /usr/bin; that would have an effect on the contents of the /bin directory outside of the app.

Lib

The lib: subsection of the requires: section is used to add a required library to a Component.

A required library is a library file that must exist in the target file system (outside the app's sandbox), and is needed by a component at runtime.

The required library must be linked with the component that's part of the the executable. The library name is specified without the leading "lib" or the trailing ".so".

requires:
{
lib:
{
foo // I need access to libfoo.so
}
}

This will result in -lfoo being passed to the linker when linking any executables that include this component. You will therefore need to have a copy of this library where the linker can find it on the build host.

Furthermore, on the target device at runtime the dynamic linker will look for this library, so the library must be made available inside the application sandbox somewhere in the dynamic linker's library search path. This can be done using the files: or dirs: subsection of the requires: section of either the .cdef or .adef file.

requires:
{
file:
{
// Make the "foo" library available inside the app sandbox (in the app's /lib directory).
/usr/lib/libfoo.so.3 /lib/
/usr/lib/libfoo.so.3.1.1 /lib/
}
}

Component

Declares this component depends on another component.

requires:
{
component:
{
foo
bar
}
}

Any app that uses a component will also use any other components that component requires, and any components they require, etc.

Specifying a dependency on another component ensures that calls to component initialization functions ( COMPONENT_INIT in C/C++ components ) are sorted in the correct order. If component A depends on component B, then component B will be initialized first.

Dependency loops are not allowed: component C can't depend on another component that (either directly or indirectly) depends on component C. The build tools detect dependency loops and report any error.

Sources

Contains a list of source code files.

If C or C++ code, one source file must implement a COMPONENT_INIT function. The framework will automatically call that function at start-up.

sources:
{
foo.c
bar.c
init.c // This one implements the COMPONENT_INIT
}

Copyright (C) Sierra Wireless Inc. Use of this work is subject to license.