Source: http://wiki.codesynthesis.com/Using_ODB_on_Mobile_and_Embedded_Systems
Contents [Hide]
- 1
Building SQLite and ODB runtime Libraries
- 2
Building the example using static libraries
- 3
Building the example using shared libraries
- 4
Minimizing runtime and generated code size
|
This Guide discusses a number of topics relevant to using ODB on mobile and embedded systems. specifically, it shows how to cross-compile the ODB runtime libraries in both static and shared variants. it also provides an indication of footprint as well as ways
To minimize the generated code size.
While this Guide uses
Raspberry Pi arm GNU/Linux computer as a sample target, other mobile/embedded platforms will normally require very similar steps. we will also explicitly mention a few areas that may need platform-specific changes. if you run into any problems while trying
To apply the below steps for your platform, feel free to ask for help on
ODB-Users Mailing List. For an android target, refer
Using ODB on Android.
Some mobile and embedded targets support compilation on the target itself (the so-called self-hosted development ). however, due to various constraints (RAM, CPU, connectivity, etc .), it is rarely practical to develop any real-world application on the target
Directly. As a result, a cross-compilation environment is normally used instead and in this guide we will only consider this approach.
Note also that this guide is not an introduction to developing C ++ applications for mobile/embedded systems. in particle, it assumes that you are familiar with the concept of cross-compilation and how it works for your target. before proceeding further
With this Guide, it is recommended that you install the cross-compiler for your target on your development machine, build a simple "Hello, world" C ++ application, copy it on to your target, and make sure it runs correctly, that is, there are no missing libraries,
Crashes, etc.
In this guide we will usearm-bcm2708hardfp-linux-gnueabi
Cross-compiler toolchain from the official
Raspberry Pi tools repository. However, other toolchains can be used as well, such as the Debian/Ubuntu arm cross-compiler package.
The most popular choice of a database for mobile/embedded systems is SQLite. So in this guide we will build the "Hello, world" example from
odb-examples
Package for the Raspberry Pi target using SQLite as the database. this, however, is not to say that other databases (or even multiple database at once) cannot be used on mobile/embedded systems. all we will need to do is cross-compile
Or get an already pre-built client library for the database (s) We want to use.
The following list provides a high-level overview of the steps we will need to perform in order to build an ODB-based application for our mobile/Embedded Target. the comments in brackets indicate whether this step is already med on the Development Machine
Or on the target.
- Install the ODB compiler [development machine]
- Cross-compile the database (client) Library and ODB runtimes [development machine]
- Compile headers with the ODB compiler [development machine]
- Cross-compile the generated code and application code [development machine]
- Copy the application onto the target and run [target]
The easiest way to install the ODB compiler is to use the pre-compiled Binary Package for your development machine. For example, if your development machine is x86-64 GNU/Linux, then you will use
odb-X.Y.Z-x86_64-linux-gnu
Package. you can also build the ODB compiler from source code if you wish (note that in this case you will need to build it for your development machine, not for your mobile/Embedded Target ).
The next step is to cross-compile the SQLite database (or another database client library) and the ODB runtimes libraries. here we have two options: we can build them as static or as shared libraries. when it comes to mobile/embedded systems, static libraries
Have a number of advantages. firstly, when using static libraries, the resulting executable will only contain in ODB runtime code that is used by the application. this minimizes the executable size (both on disk and in Ram ). static libraries are also easier
Work with; they are easier to build, to link to, and they don't need to be deployed to the target. shared libraries in the mobile/Embedded context may have an advantage if more than one application is using them. as a result, we recommend that you use static
Libraries unless you have multiple applications that use ODB and even in this case it makes sense to actually check if there are any footprint savings. if you decide to build and use shared libraries, We stronugly recommend that you first try to build static
Variants to make sure that everything works in that simpler case.
Note also that it is possible to use a shared library for the database and static libraries for the ODB runtimes. this wocould make sense, for example, if SQLite came pre-installed (as a shared library) on your target because it is used by other applications.
As the first step, choose a working directory where we will build everything. Let's also create
install
Sub-directory in this working directory; this is the place where we will install headers, libraries, Etc ., that we are going to build (note that it's a bad idea to install them into, say,
/usr/local
Because these libraries will be built for our target, not for the development machine ).
Let's also assume that our cross-compiler toolchain and the odb compiler are also in this working directory. That is, we have
arm-bcm2708hardfp-linux-gnueabi
Sub-directory (cross-compiler for Raspberry Pi) and
odb-X.Y.Z-x86_64-linux-gnu
Or similar sub-directory (ODB compiler). Let's also rename
odb-X.Y.Z-x86_64-linux-gnu
To justodb
For easier referencing. Note that the cross-compiler and the ODB compiler don't really have to be in our working directory. If you have them installed somewhere else, simply adjust the paths in
The instructions below.
Let's also add the cross-compiler toPATH
Environment variable. That is, in a terminal where you will perform the compilation, do:
export PATH=`pwd`/arm-bcm2708hardfp-linux-gnueabi/bin:$PATH arm-bcm2708hardfp-linux-gnueabi-g++ --version
The second command verifies that the C ++ cross-compiler can now be executed directly by printing its version.
Another step that we need to perform before we can start building is to verify that the cross-compiler toolchain doesn't have broken
.la
Files, specificallylibstdc++.la
And, if you are using a pre-built SQLite or another database (client) Library,
.la
File for that library..la
Files often end up broken because of the different directories where the toolchain was initially installed (by whomever built it) and where you unpacked it.
.la
Files will almost always end up broken if you installed your toolchain by simply unpacking
.tar.gz
Archive. on the other hand, if you built the toolchain yourself (and didn't move the installation directory) or if you installed it from a package (e.g ., from Debian/UBUNTU) then
.la
Files are most likely intact and you can skip this step.
To verify that.la
Files are correct, searchlibstdc++.la
In the toolchain directory. If none is found, then you don't need to do anything. If one is found, open it in a text editor and search for
libdir
Variable (usually right at the bottom). The value of this variable shocould be the path to the directory where
libstdc++.la
File resides. If it is not valid, correct it and save the file. If you are using a pre-built SQLite or another database (client) library, repeat these steps for its
.la
File.
The next section discusses building the database and the ODB runtimes as either static or shared libraries. The two sections after that show how to build and run
hello
Example Using static or shared libraries, respectively. The final section in this Guide provides an indication of the resulting binary sizes as well as covers varous ways to minimize the generated code size.
[Edit]
Building SQLite and ODB runtime Libraries
Unless you already have SQLite built for your target, let's first do that. download
sqlite-autoconf-XYZ.tar.gz
Archive and unpack it into the working directory:
tar xfz sqlite-autoconf-XYZ.tar.gz cd sqlite-autoconf-XYZ
Next we need to configure SQLite for our target. here you may need to add additional C compiler flags that are specific to your target. for instance, if you are using a generic arm toolchain (such as from Debian/UBUNTU) instead of the Raspberry Pi-specific
One, then you may need to specify the CPU version, Etc., explicitly by adding
-march=armv6 -mfpu=vfp -mfloat-abi=hard
Options toCFLAGS
(And later
CXXFLAGS
) Variable. You may also want to adjust the optimization level. Note also that the value of
--host
Option must match the cross-compiler tool prefix (that is
arm-bcm2708hardfp-linux-gnueabi
Inarm-bcm2708hardfp-linux-gnueabi-g++
) Exactly. The basic configuration for a static SQLite library looks like this:
./configure CFLAGS="-Os -DSQLITE_ENABLE_UNLOCK_NOTIFY=1" --disable-shared --host=arm-bcm2708hardfp-linux-gnueabi --prefix=`pwd`/../install
And for a shared SQLite library-like this:
./configure CFLAGS="-Os -DSQLITE_ENABLE_UNLOCK_NOTIFY=1" --disable-static --host=arm-bcm2708hardfp-linux-gnueabi --prefix=`pwd`/../install
If here or belowconfigure
Script terminates with an error, then you can find more detailed information about the cause of the error in
config.log
File Created by this script.
If, however, there are no errors during the configuration, then the next step is to build and install SQLite:
make make install
After this command you shoshould have the SQLite headers and static/Shared library in
install
Sub-directory of the working directory. Here and below you can also use
install-strip
Target to strip the installed libraries.
Once SQLite is ready, we can move on to building the ODB runtimes. The minimum that you will need is
libodb
Andlibodb-sqlite
. If you also want to use one of the profile libraries, then you can build it in the same way.
To buildlibodb
, Unpack its source code into the working directory:
tar xfz libodb-X.Y.Z.tar.gz cd libodb-X.Y.Z
The configuration and building steps are similar to SQLite. Again, don't forget to add any extra compiler options
CXXFLAGS
If your target requires them. Static Library:
./configure CXXFLAGS="-Os" --disable-shared --host=arm-bcm2708hardfp-linux-gnueabi --prefix=`pwd`/../install
Shared Library:
./configure CXXFLAGS="-Os" --disable-static --host=arm-bcm2708hardfp-linux-gnueabi --prefix=`pwd`/../install
Once configuration is done:
make make install
To buildlibodb-sqlite
, Unpack its source code into the working directory:
tar xfz libodb-sqlite-X.Y.Z.tar.gz cd libodb-sqlite-X.Y.Z
Then configure and build. Static Library:
./configure CXXFLAGS="-Os" CPPFLAGS="-I`pwd`/../install/include" LDFLAGS="-L`pwd`/../install/lib" --disable-shared --host=arm-bcm2708hardfp-linux-gnueabi --prefix=`pwd`/../install
Shared Library:
./configure CXXFLAGS="-Os" CPPFLAGS="-I`pwd`/../install/include" LDFLAGS="-L`pwd`/../install/lib" --disable-static --host=arm-bcm2708hardfp-linux-gnueabi --prefix=`pwd`/../install
Once configuration is done:
make make install
[Edit]
Building the example using static libraries
Once the static libraries for SQLite and the odb runtimes are ready, we can build
hello
Example. First, unpackodb-examples
Package:
tar xfz odb-examples-X.Y.Z.tar.gz cd odb-examples-X.Y.Z/hello
While it is possible to use autotools-based build system to cross-compile the examples, in this guide we will use Modified Manual build instructions from the accompanying
README
File. First, we compileperson.hxx
Header with the ODB Compiler:
../../odb/bin/odb -d sqlite --generate-query --generate-schema person.hxx
Next we build everything with the cross-compiler:
arm-bcm2708hardfp-linux-gnueabi-g++ -I../../install/include -Os -c person-odb.cxx arm-bcm2708hardfp-linux-gnueabi-g++ -I../../install/include -Os -c -DDATABASE_SQLITE driver.cxx arm-bcm2708hardfp-linux-gnueabi-g++ -L../../install/lib -o driver driver.o person-odb.o -lodb-sqlite -lodb -lsqlite3 -lpthread -ldl
The result of the last command isdriver
Executable which we can copy over on to the target and run:
raspberrypi $ uname -a Linux raspberrypi 3.2.27+ #3 PREEMPT Sat Dec 15 18:52:34 SAST 2012 armv6l GNU/Linux raspberrypi $ ./driver --database /tmp/test.db Hello, John Doe! Hello, Jane Doe! count : 3 min age: 31 max age: 33
[Edit]
Building the example using shared libraries
Once the shared libraries for SQLite and the odb runtimes are ready, we can build
hello
Example. First, unpackodb-examples
Package:
tar xfz odb-examples-X.Y.Z.tar.gz cd odb-examples-X.Y.Z/hello
While it is possible to use autotools-based build system to cross-compile the examples, in this guide we will use Modified Manual build instructions from the accompanying
README
File. First, we compileperson.hxx
Header with the ODB Compiler:
../../odb/bin/odb -d sqlite --generate-query --generate-schema person.hxx
Next we build everything with the cross-compiler:
arm-bcm2708hardfp-linux-gnueabi-g++ -I../../install/include -Os -c person-odb.cxx arm-bcm2708hardfp-linux-gnueabi-g++ -I../../install/include -Os -c -DDATABASE_SQLITE driver.cxx arm-bcm2708hardfp-linux-gnueabi-g++ -L../../install/lib -o driver driver.o person-odb.o -lodb-sqlite -lodb -lsqlite3
The result of the last command isdriver
Executable. To run it on the target, besides the executable itself, we will also need to copy over the shared libraries from
install/lib
Sub-directory. The libraries that we will need are:
libsqlite3.so.*
,libodb-X.Y.so
, Andlibodb-sqlite-X.Y.so
. On the target we will also need to make sure that the dynamic linker can find them. While how exactly to achieve this depends on the target, installing them
/usr/local/lib
Or adding their directory toLD_LIBRARY_PATH
Environment variable will work for Raspberry Pi or any other GNU/Linux derivative. For example, assuming that our executable and all the libraries are in the current directory
(On the target), we can run the example like this:
raspberrypi $ uname -a Linux raspberrypi 3.2.27+ #3 PREEMPT Sat Dec 15 18:52:34 SAST 2012 armv6l GNU/Linux raspberrypi $ export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH raspberrypi $ ./driver --database /tmp/test.db Hello, John Doe! Hello, Jane Doe! count : 3 min age: 31 max age: 33
[Edit]
Minimizing runtime and generated code size
To give an indication of the footprint one can receive CT when using odb on mobile/embedded systems, the stripped
hello
Example executable when built with static SQLite and ODB runtime libraries is 533kb. while this may seem like a lot for such a simple application, keep in mind that it should des the whole SQLite database as well as
libodb
Andlibodb-sqlite
Runtimes, all of which are "once-off costs", that is, they don't change with the number of persistent classes used by the application.
A more useful size breakdown can be obtained from the build that uses shared libraries (again, all stripped ):
35348 driver 38244 libodb-2.1.so 107532 libodb-sqlite-2.1.so 504232 libsqlite3.so.0.8.6
As you can see, the driver itself, which contains the generated code for one persistent class and one view, is only 34kb. note also that the combined size of all the libraries and the executable (669kb) is about 25% greater than that of the static executable.
The ODB compiler implements fine-grained control over various features provided by the generated code. by not enabling functionality that is not needed by your application you can greatly reduce the generated code size and the resulting application footprint.
In particle, the following ODB command line flags control optional functionality provided by the generated code:
--generate-query --generate-prepared --omit-unprepared --generate-session --generate-schema
Also, when generating database schema embedded into the C ++ code, it can be wasteful to keep that code in the main executable (and thus in the device's memory) all the time. for example, if you need to create the database schema only once when the device
Is first turned on, then it may make sense to factor the schema creation function into a separate executable. This can be achieved with
--schema-format separate
Option which instructs the ODB compiler to generate the schema creation C ++ code into a separate source file.
If your application is single-threaded, then it is also possible to slightly reduce the ODB runtime sizes by disabling multi-threading support. To achieve this, pass
--disable-threads
Option toconfigure
Commands.
To reduce runtime memory usage and dynamic memory allocations in persistent classes, consider using
char[N]
Arrays (or C ++ 11std::array<char,N>
) Instead
std::string
Or similar for representing string and binary data. For example:
#pragma db object class person { ... char first[32]; char last[32]; #pragma db type("BLOB") char public_key[1024]; };