//
// Copyright (c) 2017 LAAS/CNRS
// All rights reserved.
//
// Permission to use, copy, modify,  and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE  SOFTWARE  IS  PROVIDED  "AS  IS" AND  THE  AUTHOR  DISCLAIMS  ALL
// WARRANTIES  WITH  REGARD  TO   THIS  SOFTWARE  INCLUDING  ALL  IMPLIED
// WARRANTIES  OF MERCHANTABILITY  AND  FITNESS. IN  NO  EVENT SHALL  THE
// AUTHOR BE  LIABLE FOR ANY SPECIAL, DIRECT,  INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA OR
// PROFITS,  WHETHER  IN  AN  ACTION  OF CONTRACT,  NEGLIGENCE  OR  OTHER
// TORTIOUS  ACTION, ARISING  OUT OF  OR IN  CONNECTION WITH  THE  USE OR
// PERFORMANCE OF THIS SOFTWARE.
//
//                                      Anthony Mallet on Thu Sep 14 2017
//

ifdef::sidebartoc[]
include::_Sidebar.adoc[]
endif::[]

== Working with external software

This tutorial covers how to use external software in your component.

Depending on how the external software is packaged, two methods must be
considered:

 * using `pkg-config`
 * manually describing the dependency

If the external software provides `pkg-config` files, the first method is
simpler to setup and should be used. The second method is more general, but
requires a bit more code to be added to your component build system.

=== Using pkg-config

Adding a `pkg-config` dependency is achieved by declaring a
<<../dotgen/grammar#dotgen-rule-component-property,`codels-require`>>
attribute in your component description.

For instance, to add a dependency on `eigen3` with a version greater than
`3.2`, add the following line to the `.gen` file of the component:

[source, c]
.example.gen
----
        component example {
          [...]
          codels-require "eigen3 >= 3.2";
          [...]
        };
----

Simple and effective, that is all what is required.

To check that a dependency can be found by `pkg-config` you can either check
its documentation, or just run:


[source, shell]
----
        $ pkg-config --modversion eigen3
        3.2.92
        $
----

If this succeeds, you can use the pkg-config method.


=== Manually describing the dependency

When the external software cannot be discovered by `pkg-config`, it is
necessary to manually amend the component build system so that it can locate
where the dependency is installed. How this can be done highly depends on the
software in question, but this section provides some general guidelines that
should be useful for a majority of software.

==== Simple scenario

Usually, you need to locate both header files, that are to be included in your
code, and libraries to link with. In `autoconf` syntax, this can be achieved by
adding the following bits to the `configure.ac` file generated by the skeleton
template.

[source, shell]
.configure.ac
----
        [...]
        # Dependency on 'foo'. Check that header.h and function foo_init() in
        # libdependency.so are present.
        AC_CHECK_HEADER([header.h],,[
          AC_MSG_ERROR([Could not find foo header.h file.])])
        AC_CHECK_LIB([dependency], [foo_init],,[
          AC_MSG_ERROR([Could not find foo dependency library.])])
        [...]
----

Then, in the automake file `codels/Makefile.am`, you need to link with the
library:

[source, makefile]
.codels/Makefile.am
----
        [...]
        libexample_codels_la_LDFLAGS+=	-ldependency
        [...]
----

If the dependency is installed in a non-standard location, you may need to pass
additional flags to the `configure` script when compiling your component.

For instance, if the 'foo' dependency is installed in `/opt/foo`, you may run
the `configure` script of your component like so:

[source, shell]
----
$ ../configure CPPFLAGS="-I/opt/foo/include" \
               LDFLAGS="-L/opt/foo/lib -R/opt/foo/lib"
----

TIP: The `-R/opt/foo/lib` sets the runtime library path in the component
binary, so that you can run it without bothering with runtime variables such as
`LD_LIBRARY_PATH`.


==== Using an external {cpp} library with no C symbol

The previous method works well for depencencies in which you can call a `C`
function. If you need to link with a `{cpp}` library that exports no `C`
symbol, you should use something similiar to the following snippet in your
`configure.ac` file:

[source, shell]
.configure.ac
----
        [...]
        # Dependency on 'tinyxml'. Check that libtinyxml.h and class
        # TiXmlDocument in C++ libtinyxml.so are present.
        AC_LANG([C++])
        AC_CHECK_HEADER([tinyxml.h],,[
          AC_MSG_ERROR([Could not find tinyxml.h file.])])

        AC_MSG_CHECKING([TiXmlDocument in libtinyxml])
        SAVED_LDFLAGS="$LDFLAGS"
        LDFLAGS="$LDFLAGS -ltinyxml"
        AC_LINK_IFELSE(
          [AC_LANG_PROGRAM([#include <tinyxml.h>], [TiXmlDocument dummy()])],
          [AC_MSG_RESULT([yes])],
          [AC_MSG_RESULT([no])
           AC_MSG_ERROR([Could not find libtinyxml dependency library.])])
        LDFLAGS="$SAVED_LDFLAGS"
        [...]
----

The `codels/Makefile.am` shoud be modified like in the simple scenario above:

[source, makefile]
.codels/Makefile.am
----
        [...]
        libexample_codels_la_LDFLAGS+=	-ltinyxml
        [...]
----
