Component.cdef
files can contain these sections:
Used to describe collections of information (fields) that can be exchanged with cloud services like AirVantage.
The assets section can hold multiple assets, each asset is given a name.
An asset can be:
variables
values that are readable by the server; writable by the client.settings
values that are writable by the server; readable by the client.commands
that are custom commands sent from the server and executable by the client.The variable and setting fields can have an associated data type and optionally a default value. The current data types supported are bool, int, float, and string
.
In this code sample, helloWorld
app has one setting (the message to be logged), one variable (records how many times a message has been logged). The app also exposes two commands: one to log the currently configured message and one to log the default message.
The assets
definition looks like this:
assets: { message = { settings: { string greeterMessage = "Hello world." // When requested log this message. } variables: { int greetCount // How many times has a greeting been logged? } commands: { greetNow // Log the message recorded in greeterMessage. standardGreeting // Use "Hello world," instead of what is set in greeterMessage. } } }
Once you've defined your assets, it's up to the app to instantiate instances of them at run-time using the AirVantage Data API. Also see Manage AirVantage Data.
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 - any combination of one or more of the following letters, enclosed in square brackets:
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.
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.
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.
/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:
root
on all files in the app.setuid
bit is cleared on everything in the app.Provides a way to specify command-line arguments to pass to the compiler when compiling C source code files.
Flags are separated by whitespace.
Provides a way to specify command-line arguments to pass to the compiler when compiling C++ source code files.
Flags are separated by whitespace.
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.
Specifies the number of memory pool blocks that each memory pool should contain.
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.
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()
.
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).
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.
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:
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.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 Interface Definition Language for more information, or try it and have a look at the generated header files.
The requires:
section specifies things the component needs from its runtime environment.
It can contain various subsections.
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:
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.
To rename the interface, an interface name followed by an equals sign ('=') can be added in front of the .api file path.
Multiple instances of the same API listed in the api:
section must have unique instance names, and appear as separate functions with different prefixes.
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.
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.
Declares:
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:
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 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/ } }
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: { // Program uses /var/run/someNamedPipe which it calls /var/run/externalPipe. /var/run/someNamedPipe /var/run/externalPipe } }
Declares:
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 and a set of optional access permissions:
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: { device: { // I get read-only access to the SPI port. [r] /dev/sierra_spi /dev/sierra_spi // I get read-only access to the NMEA port. /dev/nmea /dev/nmea // I get read and write access to the I2C port. [rw] /dev/sierra_i2c /dev/ } }
Note that if a hot-plug device is unplugged and plugged back in, the app must be restarted before it can access the device.
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: { device: { /dev/ttyS0 /dev/port1 // Program uses /dev/port1, but UART0 is called /dev/ttyS0. } }
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:
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 } }
/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.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".
This will result in -lfoo
being passed to the linker when linking any executables that include this component. You will need a copy of this library where the linker can find it on the build host.
Also, on the target device at runtime, the dynamic linker will look for the library so it must be made available inside the app 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.
Declares this component depends on another component.
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.
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.
Copyright (C) Sierra Wireless Inc. Use of this work is subject to license.