Search by Tags

How to Import a C/C++ Application to Torizon

 

Article updated at 27 Feb 2021
Subscribe for this article updates

Select the version of your OS from the tabs below. If you don't know the version you are using, run the command cat /etc/os-release or cat /etc/issue on the board.



Remember that you can always refer to the Torizon Documentation, there you can find a lot of relevant articles that might help you in the application development.

Torizon 5.0.0

Introduction

If you have an existing project and you want to migrate it to Torizon, you may wonder if there are any specifics or guides on how to do it. This article selects existing open-source C/C++ projects that are not necessarily commonly used on embedded and shows how to import them in Torizon, using the Visual Studio Code Extension for Torizon to build and debug them.

In this article, we will show some "tricks" and ways to work around build and runtime issues. The applications chosen as samples are medium-sized open source applications that may match the size of a typical customer's application on embedded devices.

The information may not apply directly to your own scenario, but reading through the article should give you good hints about how to handle different issues you may experience. Keep in mind that there are various frameworks to build a C/C++ project, for instance, the Makefiles, autotools, cmake, or even invoking the compiler directly. If the framework you use is not described, please contact us as want to know more about your requirements.

Example

To illustrate the process of importing a C++ application to Torizon, the following example will be covered in this article:

  • Autotools example: Fuse emulator for the Sinclair Zx-Spectrum

This article complies to the Typographic Conventions for Torizon Documentation.

Prerequisites

Guided Example: Autotools - Fuse Emulator

To illustrate the process of importing a C++ application to Torizon, we will show the process of importing a project written in C to Torizon. As an example, we will import the Fuse emulator project.

Fuse is an emulator for the Sinclair Zx-Spectrum, a home computer that was very popular in the 80s. It is written in C and has a UI developed with Gtk. Debian already provides packages for Fuse but in this article we will ignore them and show how to build it from source code.


  • As an example, we will import Fuse Emulator Project to Torizon - http://fuse-emulator.sourceforge.net/

    As an example, we will import Fuse Emulator Project to Torizon - http://fuse-emulator.sourceforge.net/

Most of the features are encapsulated in a library named libspectrum. These are the stats of the full codebase (fuse and libspectrum) generated by cloc:

Language files blank comment code
C 273 20781 10310 87191
C/C++ Header 136 2793 3104 5559
Perl 23 1512 561 4401
m4 9 303 171 2766
make 33 311 680 1236
Windows Resource File 15 175 272 494
yacc 1 51 23 245
lex 1 40 21 101
Bourne Shell 3 24 35 34
SUM: 494 25990 15177 102027

It’s around 100k lines of code, so it can be reasonably considered a medium-size project.

Download the Source-Code

Download fuse source-code using Git:

$ git clone https://git.code.sf.net/p/fuse-emulator/fuse fuse-emulator-fuse

You will also build libspectrum, as it will be useful to have both codebases during debugging. Clone it inside the folder where we downloaded fuse:

$ cd fuse-emulator-fuse
$ git clone https://git.code.sf.net/p/fuse-emulator/libspectrum fuse-emulator-libspectrum

If you are working on a Windows PC you must ensure that all files are using unix-style line ending (LF) and not the Windows one (CRLF). To convert all files to Unix line ending you may use WSL. Open a prompt by running wsl.exe, cd to the folder where you stored the code and run:

find -type f | grep -Fv ".git" | xargs dos2unix

Import the Application Using the VS Code Extension for Torizon

The first operation required to migrate fuse to Torizon is creating a configuration for it, this can be done with the “Import an existing C/C++ application” command of the VS code extension. You can follow those steps:

  • Open the fuse base source folder (fuse-emulator-fuse)
  • Define the application name (fuse)
  • Select autotools-based project as the project type
  • Select one of the Wayland-based platforms (in the sample we will use the 32-bit)
  • Select user as torizon (default)
  • Select debug configuration
  • Provide the relative path (bin/fuse) where main executable binary will be installed into.

The extension creates a build container, reloads VSCode and runs inside this container using the Remote-containers extension from Microsoft.

At this point the only original source file that has been changed is .gitignore. The extension adds ignore entries for its working folders, making it easy to add the configuration files to your Git repository.

After opening the folder, Visual Studio will execute the configuration tasks, and this will generate an error:

checking for LIBSPECTRUM... no
configure: error: in `/workspaces/fuse-emulator-fuse':
configure: error: The pkg-config script could not be found or is too old.  Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.

Alternatively, you may set the environment variables LIBSPECTRUM_CFLAGS
and LIBSPECTRUM_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.

To get pkg-config, see <http://pkg-config.freedesktop.org/>.
See `config.log' for more details
The terminal process terminated with exit code: 1

We are going to solve this issue in the coming steps.

Add Build Dependencies

Fuse uses some external libraries, so those should be added to the SDK and runtime containers to be able to build and run it. Fuse can use GTK+, SDL, libsvga or native X11 for its UI. GTK is the default option, so it will be built using that library, which also makes it relatively easy to run it using Wayland. To learn more about adding build and runtime dependencies, we recommend you to read the correspondig sections on How to do C/C++ Development on Torizon and C/C++ Development and Debugging on TorizonCore Using Visual Studio Code.

After searching inside the documentation (README and INSTALL mostly) you can get a list of development libraries: glib, audiofile and gtk-3. Some tools are needed during build (yacc and flex), so additional packages will have to be installed inside the SDK container. Keep in mind that this process depends on the project you are migrating to Torizon: sometimes the dependencies are clearly stated in the project documentation, other times you will have to either adopt a trial and error approach where you build the project and fix the errors, or read the source code and understand what libraries and tools are used.

You need to find the matching development packages for the corresponding version of Debian that the Torizon Container is based from - for instance Debian Bullseye for Torizon 5. Searching on packages.debian.org is usually one of the easiest ways; another possibility is to run a Debian Containers for Torizon in interactive mode and use a command-line tool as apt-cache search combined with grep to filter the results. Ensure that you install the right version for the architecture you choose, either armhf or arm64 (we will use armhf in this sample).

You can change our configuration:

  • Select Torizon icon in the Visual Studio Code activity bar.
  • Go to the CONFIGURATION panel.
  • Set the property devpackages to:
libglib2.0-dev:armhf libaudiofile-dev:armhf libgtk-3-dev:armhf bison flex

After this operation, Visual Studio Code will ask you if you want to rebuild the SDK container and add the new packages. Make sure that all files in the editor are saved before proceeding, and select the option to rebuild and reload the SDK container. After rebuild you'll get the same error from the configuration task, but don't worry, it's going to be fixed soon.

Configure the Build Tasks

Having to build libspectrum makes it a bit more complex because we must configure the Torizon project to build it before building the main Fuse codebase.

When using autotools you need to first configure the build, generating the actual makefiles, and then build it using make. Those steps are defined in every project Visual Studio Code tasks, in .vscode/tasks.json. By default, the extension generates two tasks named configure_debug and configure_release, both depending on the autoreconf task, that generates the configure script.

Having libspectrum in the picture requires that you define some additional tasks to configure and build it. You may also have to define debug and release variants, but at the moment we keep it simple and focus on a single configuration:

tasks.json
        {
            "label": "autoreconf_libspectrum",
            "command": "autoreconf",
            "type": "shell",
            "args": [
                "--install",
                "--force"
            ],
            "options": {
                "cwd": "${workspaceFolder}/fuse-emulator-libspectrum"
            },
            "problemMatcher": [],
            "group": "none"
        },
        {
            "label": "configure_libspectrum",
            "command": "./configure",
            "type": "shell",
            "args": [
                "--prefix",
                "${workspaceFolder}",
                "--host",
                "${command:torizon.ccpp.getHostTriplet}"
            ],
            "options": {
                "cwd": "${workspaceFolder}/fuse-emulator-libspectrum"
            },
            "problemMatcher": [],
            "group": "none",
            "dependsOn": [
                "autoreconf_libspectrum"
            ]
        },
        {
            "label": "build_libspectrum",
            "command": "make",
            "type": "shell",
            "args": [
                "install-strip"
            ],
            "problemMatcher": {
                "base": "$gcc"
            },
            "options": {
                "cwd": "${workspaceFolder}/fuse-emulator-libspectrum",
                "env": {
                    "CFLAGS": "-g",
                    "CXXFLAGS": "-g"
                }
            },
            "group": "build",
            "dependsOn": [
                "configure_libspectrum"
            ]
        },

We also need to add build_libspectrum as a dependency of our existing configure tasks:

tasks.json
        {
            "label": "configure_debug",
            "command": "./configure",
            "type": "shell",
            "args": [
                "--prefix",
                "/${command:torizon.getAppName}",
                "--host",
                "${command:torizon.ccpp.getHostTriplet}",
                "CFLAGS=-g",
                "CXXFLAGS=-g",
                "LDFLAGS=-g"
            ],
            "problemMatcher": [],
            "group": "none",
            "dependsOn": [
                "autoreconf",
                "build_libspectrum"
            ]
        },
        {
            "label": "configure_release",
            "command": "./configure",
            "type": "shell",
            "args": [
                "--prefix",
                "/${command:torizon.getAppName}",
                "--host",
                "${command:torizon.ccpp.getHostTriplet}"
            ],
            "problemMatcher": [],
            "group": "none",
            "dependsOn": [
                "autoreconf",
                "build_libspectrum"
            ]
        },

You'll also have to configure pkgconfig to search into libspectrum build folder to find the .pc file describing the newly build library. To do this you have to configure an environment variable. This can be done by adding an "options" entry at the beginning of tasks.json.

tasks.json
    "version": "2.0.0",
    "options": {
        "env": {
            "PKG_CONFIG_PATH": "${env:PKG_CONFIG_PATH}:${workspaceFolder}/lib/pkgconfig"
        }
    },
    "tasks": [

Visual Studio Code won't automatically reload tasks, so you have to open the command palette and run the task Developer: Reload Window command to reload the editor and run your new tasks.

Pressing Ctrl + B will launch the build task, this should complete successfully.

Configure the Deployment Tasks

Now you need to prepare the application for deployment. The Torizon extension requires that all the files used by the application are deployed under a subfolder under appconfig_*/work.

Note: During debug those files are copied to the device directly, so there is no need to rebuild the debug container, and during release those files are added inside the release container.

The regular deploy task relies on the make install command. This won’t deploy libspectrum, so we will need to create an additional task for this:

tasks.json
       {
            "detail": "deploy application to work folder",
            "label": "deploy",
            "command": "make",
            "args": [
                "install"
            ],
            "type": "shell",
            "options": {
                "env": {
                    "DESTDIR": "${workspaceFolder}/${config:torizon.appfolder}/work"
                }
            },
            "group": "none",
            "dependsOn": [
                "deploy_libspectrum"
            ]
        },
        {
            "detail": "deploy libspectrum to work folder",
            "label": "deploy_libspectrum",
            "command": "install",
            "args": [
                "-D",
                "-t",
                "${workspaceFolder}/${config:torizon.appfolder}/work/${command:torizon.getAppName}/lib",
                "lib/libspectrum.so.*"
            ],
            "type": "shell",
            "group": "none"
        }

You will have to execute the command Developer: Reload Window again to reload tasks.json.

If you run the deploy task, everything we need to run fuse should be under appconfig_0/work/fuse-emulator-fuse. The directory name may be different if you changed application name during configuration or added other Torizon platform configurations to the project.

Add Runtime Dependencies

The application is ready to be deployed, but what runtime dependencies do you need to run it? You need to first configure the container that will host it with the required libraries. To learn more about adding build and runtime dependencies, we recommend you to read the correspondig sections on How to do C/C++ Development on Torizon and C/C++ Development and Debugging on TorizonCore Using Visual Studio Code.

Please notice that:

  • We are not installing -dev packages, those are required for development, we just install the binaries required at runtime.
  • The container is a native Arm32/Arm64 container, so we don’t need to specify the architecture of our packages.

Set the extrapackage configuration property to add the runtime dependencies:

glib2.0 libaudiofile1 libgtk-3-0

Enable the Application to Access the Wayland Socket

Selecting a Wayland-based platform grants that the Wayland compositor is executed when our application run. Since the Wayland compositor container will also be downloaded, on first run it may take a few minutes.

To communicate with the compositor the application needs to access the Wayland socket under /tmp. We need to add /tmp to the volumes in our application configuration:

  • press the + button near volumes
  • insert /tmp as the key value, press Enter to confirm
  • insert /tmp also as value, press enter to confirm

To learn more about adding volumes and bind-mounts using our Torizon extension, read the Torizon Best Practices Guide - Storing Data Permanently.

Set the Shared Libraries Path (LD LIBRARY PATH)

libspectrum will be deployed in the /lib subfolder of fuse directory, we have to add this to the LD_LIBRARY_PATH environment variable. To set environment variables, you can use the env configuration property and set it to:

ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/#%application.appname%#/lib"

To learn more about setting environment variables using our Torizon extension, including an example, read the Torizon Best Practices Guide - Graphical User Interface (GUI).

Update MIME Database

There is another issue, discovered while debugging the application, that is related to Gtk. You’ll have to run the update-mime-dabase tool to be able to load icons. Since those icons are used in the app menus, a failure on load will prevent fuse from running.

We can execute extra commands after our packages have been installed in the target container using the buildcommands property. Set it to:

RUN update-mime-database /usr/share/mime

Now you can set a breakpoint (main entry point is in fuse.c) and press F5 to deploy and run the application. Learn more about deploy, debug and release, among other topics, in the article Visual Studio Code Extension for Torizon.

Guided Example: Qt 5 - Serial Studio

Read the blog post from May, 2021 Importing a Qt Project Into Torizon Using VS Code.