1

I'm working on a C/C++ project where I use the GNU Autotools for building. My directory structure is as follows:

..
adapter/
    libraryA/
        include/
        src/
        ..
    libraryB/
        include/
        src/
        ..            
    ..
src/
    Log.cpp
    ..

Besides libraryA and libraryB, I also have one library at the top level, call it libraryMain, which all use the same Log.cpp from src. If I'm building out-of-source with the --disable-dependency-tracking option passed to configure (see also my post here), libtool complains

libtool: link: `Log.lo' is not a valid libtool object

when linking libraryMain because a Log.lo was generated in src when building libraryA and libraryB which is then referenced by libraryMain. If I manually remove Log.lo from src and rebuild libraryMain, everything works fine because libraryMain generates its own Log.lo in build/src.

To make things more clear, my Makefile.am for libraryMain (located in src) looks as follows:

..
libcomana_@COMANA_API_VERSION@_la_SOURCES = \
    .. \
    Log.cpp
..

while the Makefile.am for libraryA (located in adapter/libraryA/src) is

..
libraryA_la_SOURCES = \
    .. \
    $(top_srcdir)/src/Log.cpp
..

I already understood that the problem is due to the libraries in adapter not placing their generated .lo files in their own directory but directly under the src directory in the project's root directory. I would actually assume that no libtool objects are placed in the source tree but rather in the build tree as is also done for libraryMain. As a first workaround, I created a symlink to src/Log.cpp in adapter/libraryA/src and adapter/libraryB/src but I think that this is probably not very elegant. I really hope I have made myself clear enough. I really appreciate any comments on this issue.

Community
  • 1
  • 1
Marcel
  • 616
  • 1
  • 5
  • 15

1 Answers1

1

Ok, I think my question might have been a bit unclear. To wrap up, I circumvented the problem by omitting the --disable-dependency-tracking option and changing the my Makefile.ams to not include any sources prefixed by $(srcdir) or $(top_srcdir) because this seems to confuse automake, at least when building out-of-source (see also my post here or this bug report). Although I don't know if this is the right place for this, I decided to set up a small example, which works correctly and avoids the problems I described in my question.

My directory structure is as follows:

autogen.sh
configure.ac
Makefile.am
adapter
    Makefile.am
    adapterA
        Makefile.am
        include
            foo.h
        src
            foo.cpp
            Makefile.am                
    adapterB
        Makefile.am
        include
            foo.h
        src
            foo.cpp
            Makefile.am
debug
include
    hello.h
m4
src
    hello.cpp
    helloconf.sh.in
    Makefile.am

The files contain the following code:

autogen.sh:

#!/bin/sh

# GNU autotools toolchain
libtoolize --automake
aclocal
autoheader
automake --add-missing --force-missing --gnu
autoconf

configure.ac:

dnl Process this file with autoconf to produce a configure script.

dnl Project name and version
AC_INIT([hello], [0.1])

dnl Library version information
AC_SUBST([HELLO_API_VERSION], [0.1])
AC_SUBST([HELLO_SO_VERSION], [0:0:0])

dnl Create a separate header with C preprocessor `#define` statements
AC_CONFIG_HEADERS([config.h])

dnl Look for auxiliary scripts in the current directory
AC_CONFIG_AUX_DIR([.])

dnl Automake initialization
dnl -Wall Show warnings
dnl -Wall Treat warnings as errors
dnl subdir-objects Place generated object files (.o) into the same as 
dnl                directory their source files, in order to avoid 
dnl                collisions when non-recursive make is used
AM_INIT_AUTOMAKE([-Wall subdir-objects])
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])

dnl Libtool settings
LT_INIT([disable-static])
AC_CONFIG_MACRO_DIR([m4])

dnl Doxygen
dnl DX_HTML_FEATURE(ON)
dnl DX_PDF_FEATURE(OFF)
dnl DX_INIT_DOXYGEN([$PACKAGE_NAME], [Doxyfile], [doc])

dnl Checks for programs
AC_PROG_CXX([g++])
AC_PROG_INSTALL
AC_PROG_LN_S
AM_PROG_LIBTOOL

dnl Use the C++ compiler for the following checks
AC_LANG([C++])

dnl Checks for libraries

dnl Checks for header files
AC_HEADER_STDC
AC_CHECK_HEADER(iostream)

dnl Checks for typedefs, structures, and compiler characteristics
AC_TYPE_SIZE_T

dnl Checks for library functions

dnl Compiler and linker flags
AC_SUBST([AM_CXXFLAGS], [-Wall]) # C++
AC_SUBST([AM_LDFLAGS], [-Wall]) # ld (Linker)

dnl Libraries
AC_SUBST([LIBS])

AC_CONFIG_FILES(
    Makefile
    adapter/Makefile
    adapter/adapterA/Makefile
    adapter/adapterA/src/Makefile
    adapter/adapterB/Makefile
    adapter/adapterB/src/Makefile
    src/Makefile
    src/helloconf.sh
)

AC_OUTPUT

Makefile.am:

# Process this file with automake to produce Makefile.in

# Independent executable script for inclusion in the distribution archive
dist_noinst_SCRIPTS = autogen.sh

# Look for additional autoconf macros in the m4 subdirectory
ACLOCAL_AMFLAGS = \
    -I m4 \
    @ACLOCAL_FLAGS@

# Subdirectories
SUBDIRS = \
    adapter \
    src 

# Entire directories copied recursively into the distribution
EXTRA_DIST = 

# Delete anything except for files required to run `./configure && make`
MAINTAINERCLEANFILES = 

# Delete anything which should not be part of the distribution
DISTCLEANFILES = 

adapter/Makefile.am:

# Process this file with automake to produce Makefile.in

SUBDIRS = \
    adapterA \
    adapterB

adapter/adapterA/Makefile.am:

# Process this file with automake to produce Makefile.in

SUBDIRS = src

adapter/adapterA/include/foo.h:

#ifndef FOO_H_
#define FOO_H_

void foo();

#endif

adapter/adapterA/src/foo.cpp:

#include <iostream>

#include "foo.h"

void foo()
{
    std::cout << "Hello, foo from adapterA" << std::endl;
}

adapter/adapterA/src/Makefile.am:

# Process this file with automake to produce Makefile.in

# Adapter .so version
ADAPTER_SO_VERSION = 0:0:0

AM_CXXFLAGS = \
    -I$(top_srcdir)/include \
    -I$(srcdir)/../include \
    @AM_CXXFLAGS@

# Library name
lib_LTLIBRARIES = libadapter-@HELLO_API_VERSION@.la

# Sources
libadapter_@HELLO_API_VERSION@_la_SOURCES = \
    ../../../src/hello.cpp \
    foo.cpp

# Linker flags    
libadapter_@HELLO_API_VERSION@_la_LDFLAGS = \
    -version-info $(ADAPTER_SO_VERSION)

# Include directory and headers
libadapter_@HELLO_API_VERSION@_la_includedir = \
    $(exec_prefix)/include/adapter/adapterA
libadapter_@HELLO_API_VERSION@_la_include_HEADERS = \
    ../include/foo.h

# Library install directory
libdir = $(exec_prefix)/lib/adapter/adapterA

adapter/adapterB/Makefile.am and adapter/adapterB/include/foo.h are identical to their A equivalents.

adapter/adapterB/src/foo.cpp:

#include <iostream>

#include "foo.h"

void foo()
{
    std::cout << "Hello, foo from adapterB" << std::endl;
}

adapter/adapterB/src/Makefile.am:

# Process this file with automake to produce Makefile.in

# Adapter .so version
ADAPTER_SO_VERSION = 0:0:0

AM_CXXFLAGS = \
    -I$(top_srcdir)/include \
    -I$(srcdir)/../include \
    @AM_CXXFLAGS@

# Library name
lib_LTLIBRARIES = libadapter-@HELLO_API_VERSION@.la

# Sources
libadapter_@HELLO_API_VERSION@_la_SOURCES = \
    ../../../src/hello.cpp \
    foo.cpp

# Linker flags    
libadapter_@HELLO_API_VERSION@_la_LDFLAGS = \
    -version-info $(ADAPTER_SO_VERSION)

# Include directory and headers
libadapter_@HELLO_API_VERSION@_la_includedir = \
    $(exec_prefix)/include/adapter/adapterB
libadapter_@HELLO_API_VERSION@_la_include_HEADERS = \
    ../include/foo.h

# Library install directory
libdir = $(exec_prefix)/lib/adapter/adapterB

include/hello.h:

#ifndef HELLO_H_
#define HELLO_H_

void hello();

#endif

src/hello.cpp:

#include <iostream>

#include "hello.h"

void hello()
{
    std::cout << "Hello, world!" << std::endl;
}

src/helloconf.sh.in:

#!/bin/bash

echo @prefix@

src/Makefile.am:

# Process this file with automake to produce Makefile.in

# Look for additional autoconf macros in the m4 subdirectory
ACLOCAL_AMFLAGS = \
    -I m4 \
    @ACLOCAL_FLAGS@

# Compiler flags
AM_CXXFLAGS = \
    -I$(top_srcdir)/include \
    @AM_CXXFLAGS@

# Library name
lib_LTLIBRARIES = libhello-@HELLO_API_VERSION@.la

# Sources
libhello_@HELLO_API_VERSION@_la_SOURCES = \
    hello.cpp

# Libraries
libhello_@HELLO_API_VERSION@_la_LDFLAGS = \
    -version-info $(HELLO_SO_VERSION)
libhello_@HELLO_API_VERSION@_la_LIBADD = @LTLIBOBJS@

# Include directory and headers
libhello_@HELLO_API_VERSION@_la_includedir = $(includedir)

libhello_@HELLO_API_VERSION@_la_include_HEADERS = \
    $(top_srcdir)/include/hello.h

# Data
helloconf_DATA = helloconf.sh
helloconfdir = $(exec_prefix)/bin
EXTRA_DIST = $(helloconf_DATA)

P.S.: I know it might have been better to upload an archive but I wanted to keep everything in one place.

Community
  • 1
  • 1
Marcel
  • 616
  • 1
  • 5
  • 15