Conan is an open source and multi-platform package manager to create and share native binaries (mostly C and C++). It is written in Python and supports Windows, Linux, and Mac.
Besides managing dependencies, conan simplifies development process by integrating with various build tools. I touched on its most basic usage in tandem with cmake in my previous blog-post titled "Building HTTP Service in C++". However, that post aimed primary towards Mac and Linux users, and developers using Windows platform were left aside.
Today, let's see how we can use conan to manage cross-platform development.
Let's start by creating a conanfile. Instead of using plaintext file, like in the HTTP service post, we use a Python script. Conan has ability to work with both types.
conanfile.py
from conans import ConanFile, CMake, tools
class MyApp(ConanFile):
name = "my_app"
version = "1.0"
license = "unlicensed"
author = "Artem Akatev"
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
generators = "cmake"
requires = "boost/1.75.0"
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
def build(self):
cmake = CMake(self)
cmake.configure(source_folder="src")
cmake.build()
Two important directives in the file are generators
and requires
. The first one contains a list of build tools, and the second a list of all dependencies. Both lists are comma delimited.
The build section (Python function) contains cmake configuration. The cmake is set to expect CMakeLists.txt
file in src folder, so let's create the file.
src/CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)
project(my_app)
add_definitions("-std=c++11")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(my_app main.cc)
target_link_libraries(my_app ${CONAN_LIBS})
Now we need some application to compile. I will "steal" an example of HTTP client from Boost Beast.
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
// Performs an HTTP GET and prints the response
int main(int argc, char** argv)
{
try
{
// Check command line arguments.
if(argc != 4 && argc != 5)
{
std::cerr <<
"Usage: http-client-sync <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
"Example:\n" <<
" http-client-sync www.example.com 80 /\n" <<
" http-client-sync www.example.com 80 / 1.0\n";
return EXIT_FAILURE;
}
auto const host = argv[1];
auto const port = argv[2];
auto const target = argv[3];
int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;
// The io_context is required for all I/O
net::io_context ioc;
// These objects perform our I/O
tcp::resolver resolver(ioc);
beast::tcp_stream stream(ioc);
// Look up the domain name
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
stream.connect(results);
// Set up an HTTP GET request message
http::request<http::string_body> req{http::verb::get, target, version};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::dynamic_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
// Gracefully close the socket
beast::error_code ec;
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
// not_connected happens sometimes
// so don't bother reporting it.
//
if(ec && ec != beast::errc::not_connected)
throw beast::system_error{ec};
// If we get here then the connection is closed gracefully
}
catch(std::exception const& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Next, open a terminal window in the project's root directory (same as the directory containing conanfile.py
), and follow these steps to build and run the application:
1. Install build dependencies
conan install . --install-folder build
2. Build binary
conan build . --build-folder build
Run your application
Mac and Linux
./build/bin/my_app google.com 443
Windows
build\bin\my_app.exe google.com 443
Top comments (1)
interesting easily the simplest build tool I've seen, you say it's a package manager as well I love to see how that works or did I miss something