Wednesday, August 13, 2014

#BB10 NDK Creating and Using Static Libraries

Update 4:

This will likely be the last time I revisit this topic. Momentics 2.1, released a while ago, now supports creating a Cascades library, static or shared.


When the library project is created the is a detailed readme.txt file included that describes how to create and use the library. It is far from automatic, but for libraries that are growing and expanding along with a set of applications it is a very useful addition to the tool. And once the library stabilizes you can copy the library and include files out of the project and add them to other application projects with the Library Wizard as explained below.

Update 1:

After some more digging I have fine tuned the procedure a bit.

Update 2:

This BlackBerry Support Community post describes the official method which will eventually be a KB article.

Update 3:

With the release of BB 10.2 beta NDK I decided to revisit this issue and completely re-tool it. I haven't found any better description of how to build static libraries that contain Cascades classes. Full disclosure, I'm still an Eclipse neophyte. You probably know a better way to do this. If you don't I found that this works if you want to sequester some of your development into a static library for code re-use all that good stuff. These instructions are for BB10 NDK 10.2 beta running on Windows 7, but it should translate fairly easily to other supported OSs. I make use of bash scripting available from Git for Windows.

This procedure assumes that your library of classes is already fully debugged.

To start, I suggest you create a directory in the NDK tree to contain your libraries. I use LocalLibs so I created G:\bbndk_102\LocalLibs and G:\bbndk_102\LocalLibs\bin. The bin directory is to hold the scripts needed to call the Qt MOC compiler, and install the libraries. I have tried to make the scripts as location agnostic as I could, though there is some hacking required to move from the Windows environment to the bash environment. You will need 5 files in the bin directory:

domoc.bat:
@ECHO OFF
%~dp0%domoc.sh

libins.bat:
@ECHO OFF
%~dp0%libins.sh

These are just stub windows batch files that call their bash counterparts:
domoc.sh:
#!/bin/bash
# To run this in Eclips on Windows use the following command
#C:\Windows\SysWOW64\cmd.exe /c <bash_script>
# eg) C:\Windows\SysWOW64\cmd.exe /c G:\bbndk\LocalLibs\bin\domoc.sh

n=$(basename $PWD)
echo $n

SCRIPT_DIR=$(echo ${BASH_SOURCE[0]} | sed -e "s/domoc.sh//")
SCRIPT_DIR=$(cd "${SCRIPT_DIR}"; pwd)
. ${SCRIPT_DIR}/MOC_Env.sh

cd src

for h in *.h 
do
 echo $h
 b=$(basename $h .h)
 if grep -q Q_OBJECT $h
 then
  "$QNX_HOST\win32\x86\usr\bin\moc.exe" $h -o moc_${b}.cpp
 fi
 mkdir -p ../public/${n}
 cp $h ../public/${n}/${b}

done

libins.sh:
#!/bin/bash

SCRIPT_DIR=$(echo ${BASH_SOURCE[0]} | sed -e "s/libins.sh//")
SCRIPT_DIR=$(cd "${SCRIPT_DIR}"; pwd)
. ${SCRIPT_DIR}/MOC_Env.sh

n=$(basename $PWD)
mkdir -p $LIB_INCLUDE
mkdir -p $LIB_TARGET/$n/Device
mkdir -p $LIB_TARGET/$n/Simulator
cp -r public/$n $LIB_INCLUDE
cp Device-Release/lib${n}.a $LIB_TARGET/$n/Device
cp Simulator-Debug/lib${n}.a $LIB_TARGET/$n/Simulator

domoc.sh goes into the source directory, checks all the .h files to see if they define a Qt or Cascades class (they have the Q_OBJECT macro) and if they do runs the Qt MOC compiler to generate the required moc_Class.cpp file. It also copies all the Class.h files into public/LibraryName/Class files which will be installed in the local library include directory by libins.sh.

libins.sh creates the required directories and copies the include files, Device-Release and Simulator-Debug versions of the libraries into the LocalLibs tree.

Finally you'll need MOC_Env.sh which sets up a number of variables to point at appropriate locations.
MOC_Env.sh:
#!/bin/bash

TARGET=target_10_2_0_339
HOST=host_10_2_0_1

SCRIPT_DIR=$(echo ${BASH_SOURCE[0]} | sed -e "s/MOC_Env.sh//")
SCRIPT_DIR=$(cd "${SCRIPT_DIR}"; pwd)
export LOCAL_LIBS=$(dirname ${SCRIPT_DIR})
export BB_NDK=$(dirname ${LOCAL_LIBS})

export QNX_TARGET=${BB_NDK}/${TARGET}
export QNX_HOST=${BB_NDK}/${HOST}
export LIB_INSTALL=${BB_NDK}/LocalLibs/
export LIB_INCLUDE=${LIB_INSTALL}/include
export LIB_TARGET=${LIB_INSTALL}/${TARGET}

Now it is time to create a static library project with File ->New->BlackBerry C/C++ Project, select Library and Static Library:

Click Next, give the library a name and set some parameters:


Click Next, select the API level and click Finish:

You want to use Cascades objects in your library so you will have to configure the project so Eclipse know you want those include files. By default they aren't included in the project:

Right click on your new project and select Properties. Then go to C/C++ General, Paths and Symbols. Select All Configurations and the Includes tab and set Language to GCC:

Then press the Add button. Select Add to All Languages (may not be needed), then add the Directory for /usr/include/qt4:


Do the same thing for /usr/include/qt4/QtCore:


Click OK, and OK again to add these paths:


Now your includes should look something like this:


Now we have to add two new build rules. These will be external commands (domoc.sh and libins.sh) that run before and after compilation respectively.

Right click on the library project and select Properties then select Builders:

Click New, select Program and click OK:

Give the builder a name, I used MOC_Builder, the click on Browse File System below Location and select domoc.bat from your LocalLibs/bin directory:

Click on Variables below Working Directory and select projec_loc then click OK:

Click on the Refresh tab and set up the refresh conditions:

Then on the Build Options tab set up the build options:

Finally click OK to complete the builder. Then select the new builder and click the Up button until it is on the top of the list:

Repeat the whole procedure again to create a LIB_Install builder (it doesn't need to refresh anything so no changes are needed on the Refresh tab):

And it stays on the bottom of the list:

Now you can put your source files in the src directory. I suggest you start out with some simple classes to make sure everything is working. You can also un-check the builders to isolate the steps for diagnosing problems. Here is my project with one simple class:

And build the Device and Simulator libraries:

After the build your project should look like this:

And your LocalLibs tree should look like this:

You can now go to a Workspace with a Project you need the library for. Right click on the Project, select Configure -> Add Library. Select External Library, click Next and set up the libraries and include path:


If something doesn't work for you, or needs a better explanation, or if you have a better idea just leave a comment below. Otherwise enjoy.

10 comments:

  1. This is great. Thanks for posting!

    How do you go about including TestClass in a cpp/header file in your primary project?

    ReplyDelete
  2. how do i do this in osx? does bb10 sdk10.2gold have any improvement on this?

    ReplyDelete
  3. how do i do this script in osx?
    does sdk 10.2 gold have any improvement on static lib?

    ReplyDelete
  4. I am not an OSX expert, but since it is based on Open BSD I would assume that there is scripting available, maybe even BASH if look.

    The 10.2 version of the SDK has many improvements including support to libraries. There are project wizards for starting new static or dynamic libraries, and it is quite easy to add a library to any project in the same Workspace as the the library. I use the project wizards to start libraries, but for only use the Workspace add for linking a library with test and validation code. When I am using a library in a project I prefer to copy the Device Release and Simulator Debug versions of the library, and the include files to a central location and use the Add External Library wizard to add them to projects. This de-couples the library code from the program code which is a best practice in development.

    Hope this helps.

    ReplyDelete
  5. Can i add images to library and read it from application

    ReplyDelete
    Replies
    1. I haven't found a way to do that, and this answer on Stack Overflow basically says you can't, but you can place assets in a public location of a BAR file.

      Delete
  6. I able to generate device-debug and device-release libraries but simulator-debug is not getting generated. What might be wrong ?

    ReplyDelete
    Replies
    1. This whole procedure is pretty much deprecated as per update number 4. If you are using the method implemented in Momentics for building static libraries, just select the simulator build target and build it.

      Delete
    2. I am doing that only but its not working

      Delete
  7. This is exactly what I needed and is working brilliantly. Thank you!

    ReplyDelete