Creating and reusing packages based on Makefiles

Conan can create packages and reuse them with Makefiles. The AutoToolsBuildEnvironment build helper helps with most of the necessary tasks.

This how-to has been tested in Windows with MinGW and Linux with gcc. It uses static libraries but could be extended to shared libraries too. The Makefiles surely can be improved. They are just an example.

Creating packages

Sources for this example can be found in our examples repository in the features/makefiles folder:

$ git clone https://github.com/conan-io/examples.git
$ cd examples/features/makefiles
$ cd hellolib

It contains a src folder with the source code and a conanfile.py file for creating a package.

Inside the src folder, there is Makefile to build the static library. This Makefile uses standard variables like $(CPPFLAGS) or $(CXX) to build it:

SRC = hello.cpp
OBJ = $(SRC:.cpp=.o)
OUT = libhello.a
INCLUDES = -I.

.SUFFIXES: .cpp

default: $(OUT)

.cpp.o:
    $(CXX) $(INCLUDES) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

$(OUT): $(OBJ)
    ar rcs $(OUT) $(OBJ)

The conanfile.py file uses the AutoToolsBuildEnvironment build helper. This helper defines the necessary environment variables with information from dependencies, as well as other variables to match the current Conan settings (like -m32 or -m64 based on the Conan arch setting)

from conans import ConanFile, AutoToolsBuildEnvironment
from conans import tools

class HelloConan(ConanFile):
    name = "hello"
    version = "0.1"
    settings = "os", "compiler", "build_type", "arch"
    generators = "cmake"
    exports_sources = "src/*"

    def build(self):
        with tools.chdir("src"):
            atools = AutoToolsBuildEnvironment(self)
            # atools.configure() # use it to run "./configure" if using autotools
            atools.make()

    def package(self):
        self.copy("*.h", dst="include", src="src")
        self.copy("*.lib", dst="lib", keep_path=False)
        self.copy("*.a", dst="lib", keep_path=False)

    def package_info(self):
        self.cpp_info.libs = ["hello"]

With this conanfile.py you can create the package:

$ conan create . user/testing -s compiler=gcc -s compiler.version=4.9 -s compiler.libcxx=libstdc++

Using packages

Now let’s move to the application folder:

$ cd ../helloapp

There you can also see a src folder with a Makefile creating an executable:

SRC = app.cpp
OBJ = $(SRC:.cpp=.o)
OUT = app
INCLUDES = -I.

.SUFFIXES: .cpp

default: $(OUT)

.cpp.o:
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

$(OUT): $(OBJ)
    $(CXX) -o $(OUT)  $(OBJ)  $(LDFLAGS)  $(LIBS)

And also a conanfile.py very similar to the previous one. In this case adding a requires and a deploy() method:

 from conans import ConanFile, AutoToolsBuildEnvironment
 from conans import tools

 class AppConan(ConanFile):
     name = "app"
     version = "0.1"
     settings = "os", "compiler", "build_type", "arch"
     exports_sources = "src/*"
     requires = "hello/0.1@user/testing"

     def build(self):
         with tools.chdir("src"):
             atools = AutoToolsBuildEnvironment(self)
             atools.make()

     def package(self):
         self.copy("*app", dst="bin", keep_path=False)
         self.copy("*app.exe", dst="bin", keep_path=False)

     def deploy(self):
         self.copy("*", src="bin", dst="bin")

Note that in this case, the AutoToolsBuildEnvironment will automatically set values to CPPFLAGS, LDFLAGS, LIBS, etc. existing in the Makefile with the correct include directories, library names, etc. to properly build and link with the hello library contained in the “hello” package.

As above, we can create the package with:

$ conan create . user/testing -s compiler=gcc -s compiler.version=4.9 -s compiler.libcxx=libstdc++

There are different ways to run executables contained in packages, like using virtualrunenv generators. In this case, since the package has a deploy() method, we can use it:

$ conan install app/0.1@user/testing -s compiler=gcc -s compiler.version=4.9 -s compiler.libcxx=libstdc++
$ ./bin/app
$ Hello World Release!