helloWorld
This starting tutorial will walk you through creating Hello
World
by:
- Create a Component
- Bundle the Component into an Exe and then an App
- Install the System onto a Target
- Test the App
Walking through this tutorial will introduce you to the basic concepts that make up the Legato Application Framework.
Sample Directories and Files
Apps have a hierarchy of folders and files, with the base folder being the name of the app. Each component is encapsulated in it's own subdirectory and will include the .c files and a Component Definition .cdef (more on how to create def files in a bit). The app directory will include an Application Definition .adef, which is used to combine all the components into executables and configure the process for the executable as well as define the configuration for your app.
Example directory structure for Hello
World:
helloWorld/ ├── helloComponent/ │ ├── Component.cdef │ └── helloWorld.c └── helloWorld.adef
Create Component
Creating a component will walk you through: Create Directory, Create C Source File, and Create cdef File
Create Directory
Create a directory for your component: the directory name will become the component name (this is done for ease of organization and becomes important when you are including multiple components):
$ mkdir helloComponent
Create C Source File
This looks similar to the original C helloWorld
program from Kernighan's and Richie's "The C Programming Language", except that:
- #include <stdio.h> is replaced with #include "legato.h"
- main() is replaced with COMPONENT_INIT
Create the file for helloWorld:
$ vim helloComponent/helloWorld.c # use any editor of your choosing
Include legato.h
First you must include legato.h. legato.h is the main legato library, and includes stdio.h along with other system headers and Legato Framework headers. It reduces the amount of time you have to spend including header files to get access to the functions and data types that you need. List of includes in "legato.h".
Also include "interfaces.h"
. interfaces.h is a C header file that automatically includes all client and server interface definition that your client imports and exports. If you need to import or export any other API you will need to explicitly include "interfaces.h" into your C or C++ code. It is not used in helloWorld now, but it is a Legato best practice to include it in all of your Legato Component C or C++ code.
#include "legato.h"#include "interfaces.h"
Component Initializer
The COMPONENT_INIT
macro is used to identify your component initializer. Every component must have a component initializer.
By using component initializers instead of having each component implement their own main() function, it's possible to run multiple components in the same executable and even share a thread between those components.
- Note
- The main process thread (the main() function auto-generated by the build tools) will automatically call the component's initializer at the appropriate time during the process start-up sequence (based on the inter-dependencies between components). If component A is used by component B, then component A's initializer will be run before component B's initializer. Then component B can safely call the API functions of component A knowing that component A has already been initialized. This won't work if both components depend on each other (directly or indirectly through other components). That's one reason why dependency loops are not permitted between components. The framework will detect inter-component dependency loops at build time and terminate the build.
Component initializers don't take any parameters and don't return anything, but they must return in order to enable the event loop. Unless it experiences a fatal error, in which case it must terminate the process with a non-zero exit code (which can be done using LE_FATAL()
, LE_ASSERT()
, etc. ).
- Note
- If a component's initializer doesn't return, the process will not be able to finish initializing all the components that have been deployed to it and the process's main thread will never process any events that are queued to it.
In our "Hello World" example, we use our component initializer to print Hello
World! to the log using LE_INFO()
. The component initializer will run LE_INFO()
at start-up. LE_INFO()
logs a message at the INFO severity level.
Add the following lines to helloWorld.c:
{LE_INFO("Hello world!");}
Your helloWorld.c should look like this:
#include "legato.h"#include "interfaces.h"{LE_INFO("Hello world!");}
That is all that our helloWorld component needs. You can now close and save the file.
The build tools figure out what language the source code is written in by looking at the filename extension. So, because our file helloWorld.c
ends in .c, the build tools will try to use a C compiler to compile it into a library when helloWorld.c gets included in an executable.
Create cdef File
Legato definition files (.sdef, .adef, .cdef and .mdef) are used as instructions to the Build Tools (mk tools) and tell the mk tools how to bind the components, apps and systems together to create the Legato Runtime Environment which then gets installed on a target.
Component definition files are used to specify the external interfaces and internal content of the components as well as defining the requirements from the system for the component (e.g; What access does the component need, what API will the component access, what external files will it access). Each component must have a corresponding Component.cdef, which must be included in the same folder as the rest of the component code.
Create a cdef
file called Component.cdef:
$ vim helloComponent/Component.cdef
Since our helloWorld component doesn't have any external interfaces and we will be using all the default build locations we can create a simple .cdef file and only need to define the
sources:
Add the following to your Component.cdef
file and then save and exit.
sources:{helloWorld.c}
You have now successfully created your first Legato Component. Congratulations!
Create App
Next lets turn your helloWorld component into an App. Apps are the execution environment for your code, they contain one ore more executables that run together in a "Sandbox". Your executables are made by combining one or more components. Processes are the running executables that are actively running in memory. Apps may expose external API's but generally contain all the components, executables, setting and files to run within it's own environment.
The Application Definition .adef builds the instructions to tell the mktools how to build your app. In the helloWorld app we need to tell the mktools to create an executable out of the helloComponent
component and then tells the app to run that executable in a process.
Create a .adef file called
helloWorld.adef:
$ vim helloWorld.adef
First we are going to define an executable named helloWorld for the helloComponent Component. This turns your source code into an executable binary.
executables:{helloWorld = ( helloComponent )}
Next we need to tell the app to start the helloWorld executable within a process.
processes:{run:{( helloWorld )}}
- Note
- If your executable has multiple components, add more components inside the parentheses after the executable name: "helloWorld = ( helloComponent helloSun helloMoon )".
For this example your .adef file will look like the following:
executables:{helloWorld = ( helloComponent )}processes:{run:{( helloWorld )}}
Build App
Now that all your files are created and you have provided a .cdef and an
.adef for your app you are ready to use the Build Tools mktools to build your app into a binary and deploy it on your target.
You should have the following directory structure for your app:
helloWorld/ ├── helloComponent/ │ ├── Component.cdef │ └── helloWorld.c └── helloWorld.adef
Before you run mkapp
make sure that you have configured the Framework for a specific target and have run bin/legs
or sourced bin/configlegatoenv
.
mkapp
is the tool that is used to build the app into a binary package that can then be installed on your target. It is important to specify the target that you are building for as mkapp
will include all the libraries and dependencies that are needed for your specific target.
- Note
- If you do not use the -t option mkapp will build for the default, localhost which is only used for testing purposes.
From the helloWorld
directory run the following command (the target in this example is the wp85):
$ mkapp -t <target> helloWorld.adef
Output:
$ mkapp -t wp750x helloWorld.adef [9/9] Packaging app
A new directory _build_helloWorld
and an update file will be created in your helloWorld
directory.
├── _build_helloWorld/ │ └── wp85 │ ... ├── helloComponent/ │ ├── Component.cdef │ └── helloWorld.c ├── helloWorld.adef └── helloWorld.wp85.update
The _build
directory is the intermediate directory where the complier will place all staging files created during the build process. It can be removed after helloWorld.wp85.update has successfully been created, though it is helpful in troubleshooting build problems.
helloWorld.wp85.update
is the package that is needed to install helloWorld
onto your target.
Install App to Target
Use app
install to install the app on your target, the app
command is available both on your dev machine and target but when using from your dev machine you must supply the IP address of your target:
$ app install helloWorld.<target>.update <ip addr>
The Framework supports an environment variable called DEST_IP, if you set this to the IP of your target most target machine commands will default to using DEST_IP and you will not have to enter in your IP address every time. To set DEST_IP:
$ export DEST_IP=192.168.2.2 # or the IP of your target
Now you can update your target by typing:
$ app install helloWorld.wp85.update # add the target IP to the command if DEST_IP is not set
Test helloWorld
The helloWorld app logs Hello
world! to the syslog every time the app is started. To test this out we need to use the logread
command to tail the logs.
SSH to your target:
$ ssh root@192.168.2.2
Run logread (on your target):
# logread -f | grep helloWorld
From your workspace: (you must have configlegatoenv
sourced, or run bin/legs
and have DEST_IP
set)
$ app status # check that <appName> is listed $ app start <appName> # e.g., app start helloWorld
In the log you should be able to see logging statements similar to:
# logread -f | grep "helloWorld" <TIME> <target> user.info Legato: INFO | supervisor[465]/supervisor T=main | app.c CreateTmpFs() 1204 | Mounted tmpfs at /legato/systems/current/appsWriteable/helloWorld/tmp. <TIME> <target> user.info Legato: INFO | supervisor[465]/supervisor T=main | app.c CreateFileLink() 1504 | Created file link '/tmp/legato/serviceDirectoryServer' to '/legato/systems/current/appsWriteable/helloWorld/tmp/legato/serviceDirectoryServer'. <TIME> <target> user.info Legato: INFO | supervisor[465]/supervisor T=main | app.c CreateFileLink() 1504 | Created file link '/tmp/legato/serviceDirectoryClient' to '/legato/systems/current/appsWriteable/helloWorld/tmp/legato/serviceDirectoryClient'. <TIME> <target> user.debug Legato: DBUG | _UNKNOWN_[1518]/<invalid> T=main | _componentMain.c _helloWorldComponent_Init() 26 | Initializing helloWorldComponent component library. <TIME> <target> user.info Legato: INFO | helloWorld[1518]/helloWorldComponent T=main | helloWorldComponent.c _helloWorldComponent_COMPONENT_INIT() 5 | Hello, world.
Stop the App (if needed):
$ app stop helloWorld # add the target IP if DEST_IP not set
Run app
--help
for all options on controlling apps on your target from your dev machine.
app
list
- lists all the installed apps on the target app
remove
<appName> removes the app from the target
Next we are going to add a second app, communicate between the two apps and build the apps into a system.
Copyright (C) Sierra Wireless Inc.