In an autoools-based build, I'd like to replace a version-controlled C file with a generated C file.
This dummy, hello world example sort-of works:
#!/bin/sh -ex
cat > configure.ac <<EOF
AC_INIT([hello], [0.01])
AC_PREREQ([2.68])
AC_CONFIG_SRCDIR([hello.c])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.11])
AC_CONFIG_FILES([Makefile])
AC_PROG_CC
AC_OUTPUT
EOF
cat > autogen.sh <<EOF
touch NEWS README AUTHORS ChangeLog COPYING
autoreconf -i
EOF
chmod +x autogen.sh
printf "
bin_PROGRAMS = hello
hello_SOURCES = hello.c
hello.c: generate
\t./generate
" > Makefile.am
cat > hello.c <<EOF
#include <stdio.h>
int main()
{
puts("hello world");
return 0;
}
EOF
cat > generate <<EOF0
#!/bin/sh
cat > hello.c <<EOF
#include <stdio.h>
int main()
{
puts("HELLO WORLD");
return 0;
}
EOF
EOF0
chmod +x generate
./autogen.sh
./configure
make -j$(nproc)
except it prevents me from doing an out-of-tree build such as:
mkdir B
cd B
../configure
make
which I can normally do in an autotools-based package.
How can I generated the C file so that out-of-tree builds continue to work?
It is important to remember that make doesn't really know much about directories; for the most part, everything is a string to it. That's not always so obvious, but it tends to really bite you when you are trying to write a build system that can handle out-of-source builds.
You provide a build rule wherein hello.c depends on your generate script, which is fine in principle. Make will even handle the dependency processing correctly in an out-of-source build, finding the generate script in the source directory. But the recipe in the build rule explicitly runs ./generate, which does not exist when you're performing an out-of-source build. Furthermore, you have a disconnect between the dependency and the build rule: 'generate' is not the same thing as './generate'.
There are two main alternatives:
In your build rules, rely only on automatic make variables (e.g. $<) to refer to dependencies. When make is performing an out-of-source build, it initializes automatic variables with viable paths.
Refer to files in the source directory via explicit paths.
To use option (1) by itself, you would need to work around the problem of how to run the generate script when it is (presumably) not in the path. You can do that by running it indirectly, via the shell. The make rule you would want in that case would be
hello.c: generate
$(SHELL) $<
That should work for either in-source or out-of-source building. The $(SHELL) make variable should be provided by the Autotools.
To use option (2), on the other hand, you would probably rely on $(srcdir) or $(top_srcdir):
# Note that the dependency name will always contain a '/':
hello.c: $(srcdir)/generate
$<
(You can also name the generate script, with path, in the build recipe, but it's usually best to avoid repeating yourself.) The $(srcdir) and $(top_srcdir) variables are also provided by the Autotools. The former refers to the directory in the source tree that corresponds to the build-tree directory we are currently building; the latter refers to the source-tree directory containing the configure script.
Of those two, I'd rate (1) a bit more in the spirit of out-of-source building, but there isn't really that much difference.
Related
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], [qub#oregonstate.edu])
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
Can successfully create makefile by run
./ $autoreconf --install
Can successfully build the project at root directory with using following commands
./ $./configure
./ $make
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?
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
I accidently fix the problem by changing the ./HelloWorld/Makefile.am to
AM_CPPFLAGS=-I%D%/../../Pet/ -I%D% -I%C%
#VPATH = ../Pet
#srcdir = #srcdir#
#VPATH = %D/Pet/
bin_PROGRAMS=hello
hello_SOURCES=%D%/../../Pet/Pet.h
hello_SOURCES+=%D%/../Pet/Pet.cpp
hello_SOURCES+=%D%/main.cpp
Note that the path of hello_SOURCES are changed and header path is different from source path. But why would this solve the problem?
I have to merge cmake's makes and makes in our build systems. The build systems are supposed to be able to work with both options make and cmake. The problem is that cmake's make exports do not contain all variables and settings, which need to be set as make in our build systems. Basically, we use three complicated build systems for cross platform development and I do not like setting everything on many places, because then it is hard to maintain the system like that.
E g. makefiles from many coders in all build system contains include common file like:
include $(PROJECT_CONF_DIR)/common/something.mk
How to solute it by cmake? I do not like modifying coders' CMakeLists.txt (max. one row solution for them) and I also do not like modifying cmake exports into make files.
Basically, I need to put somewhere in cmake command or cmake's export (the best) some link which will lead to include all 'garbage' expecting by our build tool chains.
Make sure that CMakeLists.txt can contain many cmake subprojects and libraries.
e.g. Our build system from makefiles contains something like:
directories-default:
mkdir -p $(BUILD_DIR)
mkdir -p $(OBJ_DIR)
I need to implement it somehow in cmake include.
To be able to run make directories-default after configuration, you have to create a target. You can make a target that will call a custom command, which would run the shell commands you need.
add_custom_target(directories-default COMMAND mkdir -p "dir1" COMMAND mkdir -p "dir2")
The syntax above will result in a target that is always considered out of date, ie every time you run make directories-default (or make all), the commands will be executed. If you don't want to re-run the command every time you can use a custom command:
add_custom_command(OUTPUT "dir3" "dir4" COMMAND mkdir -p "dir3" COMMAND mkdir -p "dir4")
add_custom_target(directories-default2 DEPENDS "dir3" "dir4")
Here make directories-default2 will only run the commands the first time you run it. You can also create a dependency chain of commands using the DEPENDS argument in add_custom_command.
... Sorry, this has to be a duplicate, but I'm just running across answers for people who are making their own projects and can change the build system setup.
Can I always compile a program in a separate build directory, or must it be supported by the program's build system?
For vim, you compile using: "./configure && make && sudo make install". If I'm in vim.hg.build and run "../vim.hg/configure .....", I get :
../vim.hg/configure: line 6: cd: src: No such file or directory
For git, you compile using: "make configure && ./configure && make && sudo make install". I was hoping being in git.git.build and running "make --directory=../git.git configure" would work, but that leaves the configure file in ../git.git. Well, maybe just configure left behind isn't so bad, so I then tried "../git.git/configure" which successfully created config.status, config.log, and config.mak.autogen in the build directory. But running make gives:
make: *** No targets specified and no makefile found. Stop.
... Yes, the only Makefile is in git.git itself.
I even tried symlinking the entire directory by running:
for fl in `ls -a ../vim.hg`; do
echo "$fl"
ln -s ../vim.hg/$fl $fl
done
... But, vim's configure and make only modify existing files and subdirectories, so even though I can build this way, the build directory is left with nothing more than symlinks -- no actual separation.
Go cmake!
Out-of-tree building is a feature of Autotools that requires both Autoconf and Automake.
Vim and Git both only use Autoconf and not Automake, so they can't take advantage of that feature.
As a more general answer to your question: simple Autotools projects should work with out-of-tree builds automatically. When using certain advanced features, a bit of care must be taken to ensure that out-of-tree builds continue to work. Sometimes projects don't do this.
(Running make distcheck will test out-of-tree building, so it's a good idea to run it at least once before making a release.)
We are moving from MPC to CMake.
We provide a lib with some samples. The samples are coming with makefiles.
The problem is that the makefiles, generated by cmake contains absolute paths but not relative ones:
# The main all target
all: cmake_check_build_system
cd /.../Projects/cpp_trunk && $(CMAKE_COMMAND) -E cmake_progress_start /.../Projects/cpp_trunk/CMakeFiles /.../Projects/cpp_trunk/samples/CMakeFiles/progress.make
cd /.../Projects/cpp_trunk && $(MAKE) -f CMakeFiles/Makefile2 samples/all
$(CMAKE_COMMAND) -E cmake_progress_start /.../cpp_trunk/CMakeFiles 0
So, when it is copied it's become broken.
It there any way to work it around?
UPD: I have read the FAQ, but my question is still taking place, perhaps somebody managed to get around?
What I've done to get around this sort of thing is write a small wrapper Makefile around cmake. I put the Makefile at the project root, with contents like this:
all: cmake
cmake:
[ -f build/CMakeCache.txt ] && [ "$$(pwd)" != "$$(grep 'CMAKE_HOME_DIRECTORY:INTERNAL' build/CMakeCache.txt | cut -d '=' -f 2)" ] \
&& rm -rf build || true
mkdir -p build && cd build && cmake ..
make -C build
clean:
rm -rf build
There's probably a cleaner way to do it, but it works for me:
make # build in one directory
cd ..
olddir=$(basename $OLDPWD) && rsync -ravz $olddir ${olddir}-test && cd ${olddir}-test # copy to another directory
make # running make in the new dir triggers a full rebuild
make # running make a second time in the new dir does not rebuild
The makefiles created by CMake are not part of your source code base. The CMakeLists.txt files that you use as input to CMake are part of your source code base. When you copy your source code to a different place and want to build it there, build from your source code. That means re-running CMake. (And that's your workaround.)
I've been using CMake for over ten years continuously on one project. One of the handy tricks my team has learned is that you can have multiple copies of one part of your source code base on one development host that all share the same copy of the rest of your source code base. Try doing that with relative paths! We rely on the fact that every time we build source code in a new build directory, CMake will figure out the correct paths to all the source files, which are not necessarily the same relative to the new build directory as they were in the previous build.
The build files that are generated by cmake (makefiles, ninja files, etc), are going to have hardcoded paths and other not-portable stuff in them. That's ok. Treat them as temporary files that are part of the build process. You will only version the CMakeLists.txt files, and then generate new makefiles (or whatever) on other machines or in other directories when you check it out. You can even have different people on the team using different build files - one person using makefiles, one person using eclipse+ninja, etc, all generated by cmake.
I just want to know what do I need to write in the Makefile.am to create a directory called build in the same directory where Makefile.am is.
Think about your question carefully: Do you really want to create build in the same directory as Makefile.am, or in the current working directory when configure is called? These are not always the same thing: the GNU build system is meant to support out-of-tree builds (with a potentially read-only $srcdir), so the end user should expect the following to work:
$ tar xf autofoo-1.2.tar.gz
$ mkdir autofoo-build
$ cd autofoo-build
$ ../autofoo-1.2/configure
$ make
$ sudo make install
Now, the easiest way I have found to create a directory is not to use Makefile.am at all, but instead to make config.status do it (which is the shell script that does all of the template substitutions at the end of configure, turning Makefile.in into Makefile, config.h.in into config.h and so on). In configure.ac, put the following:
AC_CONFIG_COMMANDS([mkdir], [$MKDIR_P build])
You would need to write:
build:
test -d ${srcdir}/build || mkdir ${srcdir}/build
but you really do not want to do that. The source directory should be considered read-only. If you simply want to create a directory named build in the build directory, just do
build:
test -d build || mkdir build