Search code examples
c++makefileautotoolsautoconfautomake

How to build in a separate directory with autotool


I have a working directory as below:

./
|----HelloWorld/
|----|----main.cpp
|----|----Makefile.am
|----Pet/
|----|----Pet.h
|----|----Pet.cpp
|----build/
|----configure.ac
|----Makefile.am

I would like to use the autotool to construct makefile and then build the project in the build directory.

The ./configure.ac is

#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT([Hello], [1.0], [[email protected]])
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
AC_CONFIG_SRCDIR([HelloWorld/main.cpp])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CXX
AC_PROG_CC

AC_CONFIG_FILES([Makefile])
AC_OUTPUT

The ./Makefile.am is

include HelloWorld/Makefile.am

Note that I'm using the include to make sure the exe locates at the directory where the make command runs.

The ./HelloWorld/Makefile.am is

AM_CPPFLAGS=-I%D%/../Pet/ -I%D% -I%C%
#VPATH = ./HelloWorld ./Pet

bin_PROGRAMS=hello

hello_SOURCES=%D%/../Pet/Pet.h 
hello_SOURCES+=%D%/../Pet/Pet.cpp 
hello_SOURCES+=%D%/main.cpp

In case some people would like to try on their own computer, I attach other source codes here: main.cpp

#include <stdio.h>
#include <vector>
#include "Pet.h"

int main() {

    printf("Hello World\n");

    std::vector<Pet*> all_pets;

    Pet *dog = new Pet(string("Apple"));
    all_pets.push_back(dog);

    Pet *cat = new Pet(string("Pear"));
    all_pets.push_back(cat);

    for (int i = 0; i < all_pets.size(); i++) {
        all_pets[i]->showName();
    }

    return 0;
}

**Pet.h**
#pragma once
#include <string>
using namespace std;
class Pet
{
    public:
    Pet(string name);
    ~Pet();

    void showName();
    void showIndex();

    string _name;
    int _index;
};

Pet.cpp

#include "Pet.h"

Pet::Pet(string name)
{
    _name = name;
    srand(2345);
    _index = (rand() % 100);
}


Pet::~Pet()
{
}

void Pet::showIndex()
{
    printf("Index is %d\n", _index);
}

void Pet::showName()
{
    printf("Name is %s\n", _name.c_str());
}

Problem Statement

  1. Can successfully create makefile by run
./ $autoreconf --install  
  1. Can successfully build the project at root directory with using following commands
./ $./configure   
./ $make  
  1. Get error when building in directory ./build. Commands are:
./build/ $../configure   
./build/ $make 

Got an error as below image shows:

build error image

I think this error is caused by the compiler cannot successfully find the header files. My first question is Why the AM_CPPFLAGS=-I%D%/../Pet/ -I%D% -I%C% in makefile.am cannot solve this problem?

Since the compiler will create the .o files in the build directory with making the build tree has the same subdirectory layout as the source tree. So I can fix this problem by copy the Pet.h file to \build\Pet. However, this means I always need to copy the header files to the build directory, which is not convenient.

I find some info about VPATH. So I commented out #VPATH = ./HelloWorld ./Pet in ./HelloWorld/Makefile.am. However, it will give me a new problem:

automake error image

My assumption is the VPATH setting somehow conflicts with the include makefile.am. My second question is How can I use the VPATH correctly with using include makefile?


Solution

  • Why the AM_CPPFLAGS=-I%D%/../Pet/ -I%D% -I%C% in makefile.am cannot solve this problem?

    Because %D% and %C% produce paths to the included makefile fragment relative to the makefile that includes it, not relative to the build directory. They are not intended or suited for handling out-of-source building, though when used correctly, they do not interfere with that.

    How can I use the VPATH correctly with using include makefile?

    You are overthinking the problem. Automake supports out-of-source building automatically. You don't need to (and shouldn't) set up VPATH yourself.

    You are also making trouble for yourself with the Makefile include directive. That directive definitely has good uses, but you would do better here by either consolidating everything into the top-level Makefile.am or by setting up for recursive make. You shouldn't need that %D% and %C% stuff.

    Automake will set up VPATH for you, and that takes care of locating prerequisites when you perform an out-of-source build. For the most part, you just specify paths to sources and targets relative to the location of your Makefile.am and / or configure.ac.

    Occasionally you do need to refer to the source directory, and in that case you should use the appropriate one of $(srcdir), $(top_srcdir), $(abs_srcdir), or $(abs_top_srcdir) to ensure that out-of-source builds work correctly.

    Your project layout is a bit odd, but either one of these alternatives ought to do it:


    Recursive

    Makefile.am

    SUBDIRS = HelloWorld
    

    HelloWorld/Makefile.am

    # VPATH helps *make* identify prerequisites, but the compiler doesn't know about it.
    # We therefore need to give compiler options with real paths.  But we shouldn't need 
    # any extra options to support sources that #include headers via (correct) paths expressed
    # relative to the sources' own location.
    AM_CPPFLAGS = -I$(srcdir)/../Pet
    
    # Note: builds 'hello' in subdirectory HelloWorld/ of the build directory
    bin_PROGRAMS = hello
    
    hello_SOURCES =    \
        ../Pet/Pet.h   \
        ../Pet/Pet.cpp \
        main.cpp
    

    Non-recursive

    Makefile.am

    AM_CPPFLAGS = -I$(srcdir)/Pet
    
    # Builds 'hello' directly in the build directory
    bin_PROGRAMS = hello
    
    hello_SOURCES = \
        Pet/Pet.h   \
        Pet/Pet.cpp \
        HelloWorld/main.cpp
    

    HelloWorld/Makefile.am

    (none)


    Either way, you perform an out-of-source build just as you were trying to do: change to the wanted build directory, creating it first if necessary, run the configure script from there via an appropriate path, and then proceed with make.

    $ mkdir build
    $ cd build
    $ path/to/configure
    $ make