Blog

Dependency Management for C++ (2)

Intro

Although I didn’t post anything lately, don’t worry – I’m still working on a proposal for dependency management. I arrived at the conclusion that not only dependency management needs to be taken into account and standardised. Also building software with the defined dependencies need to be taken into account when creating a standard for dependency management.

We all know that the C++ standardisation Committee goes to great lengths to provide standards that are backwards compatible. Dependency management is a different case because there is no standard yet. Implementations of tools for dependency/package management and building software do exist. But it will be undoable to create a standardisation proposal that will be backwards compatible with existing tooling and projects. They all have their own background and starting points, these tools don’t have a common ground of a previous standard. People have done a lot of intelligent and magic stuff to get dependencies fulfilled during a build (some people applied even more magic when they needed a cross platform build) and to get their toolchain build the software they wanted to have. I will not propose magic neither will I create a proposal that takes all the magic into account. A standardisation proposal for dependency and build management (hey, do you see that I’m using a broader scope now – I wil get back on that in the next paragraph) is unique in the sense that this will be the first standard for dependency and build management for the C++ ecosystem (yes, ecosystem not platform). Because it will be the first standard, backward compatibility doesn’t come into play.

Nevertheless, I will take a lot of existing good practices (and tools) into account. I take the lessons from all the things that the community has tried seriously and use the knowledge rooted in these lessons to create a standardisation proposal. As a community we know what works and what doesn’t work. But I definitely feel like I have a clean sheet of paper to start drawing.

The whole community is explicitly invited to provide me with ideas and feedback, so we can end up with a proposal that supports the majority of C++ software development.

Scope

The scope of my proposal for standardisation is declarative dependency and build management. This is a broader scope than envisioned in my first blog on this subject. The broader scope is needed, because dependency management has a strong coupling with build management. In the end dependency management is needed to create stable and reliable builds for software packages.

A package repository is not in scope, although I do understand that such functionality is needed for actual implementations. The same goes for build systems. I will not take build systems in scope. And yes I do understand that an implemented build system is needed to produce the artefacts.

The end result of the standardisation process will be a definition of ‘stuff’ to be used by tool builders and end-users (developers, etc) to create and understand tooling needed to create reliable and reproducible builds for software written in C++.

Declarative

I take the same stance as Mathieu Ropert in his article about ‘Simplifying build in c++ (part 3)’. The scripting route is a dead end from a standardisation perspective. Although scripting can be more flexible it is very difficult to specify such an approach. It is also more difficult for end users to maintain a build system based on scripting. In the previous mentioned article it is clearly stated (and I agree to what is written there in this regard) what the issue is with a scripting based build system.

The big difference between the two (ed; script vs declarative) is that the first one is much more complex. Since it’s a program by itself, it’s very hard to analyse for correctness and can’t easily be modified by a machine to handle something the author didn’t expect. The more logic and branches it contains, the higher the chance that it will fail on a particular setup or environment.

And of course I understand that underneath a declarative build system there is a program or script that uses the declaration to create the artefacts, but from the user’s perspective the declarative approach is much easier to use and understand.

First iteration

Alas, I don’t believe in first time right! The approach for creating a standardisation proposal is an iterative process. I like to start with the first iteration today and we (yes, you the reader and I) will have several iterations in the upcoming months to create a proposal for SG15.

Where to start?

A lot of things need to be addressed. It is tempting to start with discussing a file format and just start listing all items that need a setting with regard to dependencies and building. I resist that temptation an start by defining categories. These categories contain items that need to be addressed. The categories will be designed in a follow up post, for now I like to get to a list that is fairly complete.

  • Package declaration
  • Source rules
  • Default behaviour

Package declaration

When building software a lot of settings/configuration is needed to be able to create executables or binaries. All these settings are part of the package declaration. Package declarations can exist in several places. From the perspective of a dev that builds an application that has some dependencies, the first package declaration will the one that he or she creates for the application. This package declaration will contain dependencies. These dependencies itself will also have a package declaration (which can have dependencies, etc). Items in this category are about source and include organisation, C++ standard (versions supported), dependencies and dependency versions, etc.

Source rules

The rules that need to be obeyed to make sure that the sources can be used by tooling that uses the dependency and build standard. It will contain rules that will prohibit issues with name clashes when an executable or library is built with sources from several authors.

Default behaviour

This is a textual description of the standardised (expected) behaviour when declarations for items are not provided, can’t be found on the platform or when declarations of items are not compatible with declarations in dependencies that need to be fulfilled.

Next step

First of all I like to know whether these categories make sense to you as a (library) developer and tool developer?

Secondly, what do you think is missing in this list?

You can respond by giving comments on the article. Your feedback is highly valued.

Dependency Management for C++

Exciting times

After finishing my previous blog I started working on a new article on the subject of performance measurement with micro-benchmarking or perf. Title to be determined 😉

But then I took part in the Standard C++ Foundation Developer survey. And what happend after the survey made me postponing the article and writing the article you are reading now.

You can find a summary of the results on the ISOCPP website. When the summary was published I came across a tweet by Corentin about a post on medium.com about dependency management and a tweet by Bryce Lelbach. So I responded. 

So that’s out. Wait, I just created a liability for myself on a difficult subject where everyone has an opinion about. The missing easy dependency manager is bugging me for years already and that is what made me respond to it. Years of frustration got out in this one tweet. But I’m serious, as a community we should solve this. The article of Corentin gives a lot of good starting points. I agree with most of the article.

This part of the standardization process should be studied by SG15 Tooling, chaired by Titus Winters.

Approach

Before continuing reading it is advised to read the article by Corentin. I’m not going to repeat him here ;-). It is important that we all understand the difference between a package manager and dependency management. Corentin gives some description of dependency management and to me it is clear that what we need is dependency management and tooling around that. So let’s agree that we talk about dependency management from now on only!

We should not try to build a ‘one and only’ dependency manager for C++. Let’s leave the building to tool builders, just as we do with compilers. The ISO C++ Committee doesn’t build compilers, they build specifications and leave the implementation to compiler builders. In this area the same can be done, except for one part – I will get back to that later. We also shouldn’t try to build a ‘one and only’ build system. We should strive to have the build system builders to implement the dependency specification in their tooling and that is it. Existing build tooling can add the dependency management into their tooling and use the standardized dependency specification. This specification can contain a lot of aspects:

  • Libraries these sources depends upon
  • Compiler flags needed to successfully compile these sources
  • Linker flags needed to successfully link these ‘sources’
  • Architectures on which these sources can run without problems
  • Preferred compilers for these sources
  • Versioning and source information
  • Namespace information
  • Etc.

This all is still specification (we as a community should align with tool builders to arrive at a specification that they can implement). The only aspect I think is important to create and support centrally is a repository in which sources are stored. The central repository will be used by to tools to download the sources. It goes without saying the these sources must comply to the specification. I agree with Corentin that the repository must be high available and easily duplicated. I also agree that organizations can have their own in-house repository. But I disagree that this should be a commercial industry initiative. The security and reliability is super important for the success of the initiative and therefore it must be under control of the central standardization body. This repository must be the ‘single’ source of truth.

Let’s do it

I really think that dependency management is important for the future of C++ and I’m willing to contribute to that. So Titus, when do we start.

C++ Application on CloudFoundry

CloudFoundry

My employer decided to go with Pivotal CloudFoundry as one of the cloud providers for the company. I attended a launch event for all developers. Pivotal delivered a talk on using the platform and used a bit of Go and a lot of Java in the examples. Interesting, but real performance junkies want to use an application that is compiled to a binary that can run natively on the hardware(which is always virtualized in a cloud environment). At least I want to be able to do that. So I decided to use C++ to create a TimeZoneService that translates a date from a certain time zone into a date in a different time zone. I will show how to deploy this service to CloudFoundry (not only pivotal, but also IBM uses CloudFoundry as platform for their cloud offerings).

TimeZoneService

The service is rather simple. You send it a JSON with a conversion request and you get a JSON with a converted date or an error back. An example JSON for the request is:

{
"TimeZoneFrom":"Europe/London", "TimeZoneTo":"Europe/Amsterdam",
"Year":2017,
"Month":3,
"Day":20,
"HourIn24H":15,
"Minute":25 
}

You will get a result that looks like the following:

{
"ConvertedDateTime": "2017-03-20 16:25:00.000000000 CET" 
}

Development

Now the interesting stuff. I use CLion as a my IDE. CLion uses cmake for the build. I used conan package manager for a while, but I’m still not convinced that conan suits my needs. One of the libraries I use doesn’t have (all) the debug symbols in the library. While I can choose to build the library with debug symbols myself with the help of conan, I’m not able to build the library with my own compiler and linker flags. The end result is that still some of the details are missing in the library. So in the end I didn’t use conan for this project. The libraries used are Poco, Howard Hinnant’s date, Catch2 and Trompeloeil (the latter two for unit testing and mocking).

As I have no saying in what libraries are installed on the CloudFoundry platform, I should be taking care of delivering all my dependencies to the platform. The easiest way is by use static linked libraries. So you get an executable that has all dependencies available. One to the things that made delivering this more complicated is that the Poco libraries have some circular dependencies, so I changed the installation for my vagrant box (see try it yourself) to have a libPocoAlld.a library to link against.

Some research learned that the CloudFoundry platform is ubuntu based, so I know that I have to compile the TimeZoneService for linux/ubuntu to get an executable that is ABI compatible. After this research things started getting easier (as always when you get the understanding of a subject). To get an app deployed to CloudFoundry, you have to use the CF command line tool. For a native compiled binary you have to use the binary-buildpack. It is very important that your app listens to the right port. The CloudFoundry platform uses an environment variable to communicate the port to the application. When the app starts it uses the PORT environment variable for the port to listen to on localhost. It is important to use localhost instead of a dns name, because CloudFoundry decides on which host the app will be deployed. You can therefore never now in advance what hostname to use, but fortunately localhost is always available. To deploy the app on CloudFoundry, you need to execute the following command:

cf push TimeZoneService -c './TimeZoneService' -b https://github.com/cloudfoundry/binary_buildpack.git

Bummer, the app is not deployed. See the screenshot.

Failed CloudFoundry deployment
Failed CloudFoundry deployment

So I need to recompile with static linking against libgcc and libstdc++ and push the app again to CloudFoundry.

Now the app is deployed. How to access the newly deployed app depends on your CloudFoundry account and settings. You can use curl to request a time zone conversion. The example here is for my trial account.

curl -H "Content-Type: application/json" -X POST -d '{ "TimeZoneFrom":"Europe/London", "TimeZoneTo":"Europe/Amsterdam", "Year":2017, "Month":3, "Day":20, "HourIn24H":15, "Minute":25 }' timezoneservice.cfapps.io/ConvertDateTime
curl -H "Content-Type: application/json" -X GET timezoneservice.cfapps.io/AllTimeZones

Try it yourself

When you want to try it yourself, you can go to my github and clone the repository. You will get a vagrant file that contains the installation of an ubuntu version on which you can compile the app. You will need an account for a CloudFoundry cloud provider. I cannot provide you with one, but you can try to get it from pivotal or one of the other providers.

$ git clone https://github.com/hklabbers/CloudFoundryTest.git
  • Go into the vagrant folder of your just cloned repository and start the virtual machine:
$ vagrant up
  • Login to your vagrant box by issuing the following command:
$ ssh -p 2222 vagrant@localhost
  • Go into the project folder with the following command:
cd /workspace/TimeZoneService/cmake-build-debug
  • Execute CMake and make:
  •  cmake ../
  • make
  • The executables will be in the bin directory. First run the tests:
bin/TimeZoneServiceTest
  • Now you can try to run it on your cloudfoundry.
  • After logging in with cf login, you should set a target org and space cf -o ORG -s SPACE.
  • Go into the bin directory of your project, remove the TimeZoneServiceTest executable (unless you want to deploy that also, which is useless). Now you can deploy with:
cf push TimeZoneService -c './TimeZoneService' -b https://github.com/cloudfoundry/binary-buildpack.git
  • The command line push informed you were you can find your deployed app. TimeZoneService was deployed at route timezoneservice.cfapps.io.

A Microserver built with C++

Introduction


Microservices architectures are becoming more common as a deploymentmodel for componentized systems. I will not go in depth on microservice architectures in this article. Martin Fowler gives a description of the microservice architecture on his website; see the following article about microservices from Martin Fowler and James Lewis.

I will show an implementation of a microservices server. This implementation uses minimal resources. The server will be deployed separately from the services that use this server to provide services to the world.

Prerequisites

This server is based on the Poco Project libraries (version 1.6.1); see Poco Project. This server must be compiled with an C++11 compatible compiler. CMake (3.3.2) is used to prepare the makefiles (GNU make 3.81) to compile and link the microserver (and to compile and link the services that use the microserver) . The microserver software is developed on OS X Yosemite (10.10.5) with clang (700.1.81). The microserver has been tested on OS X Yosemite (10.10.5) and Ubuntu 14.04. My continuous integration system is running Jenkins on Ubuntu 14.04.

Running the microserver

After compiling you can start the microserver application. The application’s configuration must be stored in a property file (the file extension for the property file must be .properties). By default the search path for the property file is the directory in which the application executable is stored, if not found at that location then /etc/executable name is searched and after that the /etc directory is searched for the property file. A location can be given by the means of the command line argument –c or ––config with a full path to the property file. When this command line argument is given, it will have precedence over the default search paths. So with this command line argument the microserver application will not look into the default paths for a configuration file. When the microserver application is started as a daemon, it will change the current working directory to “/” (the root of the filesystem).

Note: Due to the fact that I can only develop and test under MAC OS X and linux, I didn’t make the microserver search for the configuration file in the standard ms-windows locations. Perhaps someone can test and deliver a patch (when needed) to create this functionality on windows.

In the configuration file the services are defined. Services are defined by an url, a library that implements the service and the fully qualified class that acts as the provider. A full path to the implementing library must also be given in the property file. When an URI is not defined in the configuration file the microserver will respond with a default 404 Not Found HTTP error.

The microserver can use lazyloading to load the services libraries. The lazyloading property can be defined in the configuration (properties) file. When not defined in the configuration file the default value will be false, which means no lazyloading. This (first) version of microserver cannot unload libraries. To unload libraries you need to stop and start the microserver.

See below for an example configuration file.

A status message can be requested (by for instance a loadbalancer) to get a 200 OK HTTP message. A response to a status request will contain a JSON with a status 200 (OK) message, this is the HTTP message to indicate that this server is servicing requests. A status request can be requested by requesting the following URI: http://host:port/status. Of course the host is the hostname of the server on which the microserver is deployed. The port is defined in the configuration file. When the port is not defined in the configuration file it falls back to the default port which is 9980.

Creating a library to handle requests

Class diagram
Class diagram

The microserver uses dynamic classloading from the poco libraries. The provider class must inherit from AbstractRequestHandler; as described in the poco documentation all classes loaded by a classloader must have a common base class. A base class is necessary because the microserver needs an interface to access the provider class. For the microserver this means that provider classes must inherit from AbstractRequestHandler. The provider class acts as a factory for classes that handle the actual requests. The classes that handle the actual requests must inherit from HTTPRequestHandler.

Configuration

Configuration items that (together) define a library and a provider class start with ‘microserver.library’. The following example defines a library with the name ms-hello-world-lib.

microserver.library.ms-hello-world-lib = /helloWorld
microserver.library.ms-hello-world-lib.path = /var/microservices/
microserver.library.ms-hello-world-lib.class = hello_world::MicroServerRequestHandlerProvider

It is advised to use namespaces in the library (isn’t it always) that implements the provider class. I have the preference to reuse the class name MicroServerRequestHandlerProvider for consistency. By using a namespace the actual provider classes can use the same descriptive name and be used next to each other.

The full path must be given to the path configuration item, because the dynamic class loader uses system wide predefined search paths. By using the full path name you can be sure that the dynamic class loader will find your library.

The configuration item that defines the library name (ms-hello-world-lib in the above example) also defines the URI that point to the service(s) which are provided. All URI’s that start with /helloWorld in the above example will be handled by hello_world::MicroServerRequestHandlerProvider. This means for instance that host:port//helloWorld, but also host:port//helloWorld/SaySomething will be handled by the same provider class.

Logging

Logging is defined in the configuration file. The default logger is the root logger and all loggers inherit from this logger. The logging framework of the poco project is structured as a logging hierarchy. See the poco project for more information. All microserver classes define their own logger, which inherits from the root logger. The logger name is the class name of the class in which the logger is defined. For example the logger that is defined in the class MicroServer goes by the name MicroServer. When needed you can define different log levels for different classes. See the HelloWorld example for a setup with different log levels for different classes.

The unavoidable HelloWorld example

To show the setup and working of microserver a HelloWorld example is created. To play with the HelloWorld example you can download the microserver and the HelloWorld sources from github and build the microserver and the HelloWorld library with CMake. When you want to see the microserver in action without building it yourself? Go to HelloWorld.

MicroserverHelloWorldRequestHandler.h

#ifndef __MICROSERVERHELLOWORLDREQUESTHANDLER_H__
#define __MICROSERVERHELLOWORLDREQUESTHANDLER_H__

#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Logger.h"

using Poco::Net::HTTPRequestHandler;
using Poco::Net::HTTPServerRequest;
using Poco::Net::HTTPServerResponse;
using Poco::Logger;

namespace ms_helloworld {

class MicroServerHelloWorldRequestHandler : public HTTPRequestHandler {
public:
  MicroServerHelloWorldRequestHandler();

  void handleRequest(HTTPServerRequest &request, HTTPServerResponse &response);

private:
  Logger &l = Logger::get("MicroServerHelloWorldRequestHandler");
};

} // namespace ms_helloworld
#endif //__MICROSERVERHELLOWORLDREQUESTHANDLER_H__

MicroserverHelloWorldRequestHandler.cpp

#include "MicroServerHelloWorldRequestHandler.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"

using Poco::Net::HTTPServerRequest;
using Poco::Net::HTTPResponse;

namespace ms_helloworld {

    MicroServerHelloWorldRequestHandler::MicroServerHelloWorldRequestHandler() { }

    void MicroServerHelloWorldRequestHandler::handleRequest(HTTPServerRequest &request, HTTPServerResponse &response) {
        l.information("Request from " + request.clientAddress().toString());

        response.setChunkedTransferEncoding(true);
        response.setContentType("text/html");

        std::ostream &ostr = response.send();
        ostr << "<html><head><title>MicroServerRequestHandler powered by POCO C++ Libraries</title>";
        ostr << "<meta http-equiv=\"refresh\" content=\"30\"></head>";
        ostr << "<body>";
        ostr << "Hello World! MicroServerHelloWorldRequestHandler.";
        ostr << "</body></html>";
    } 
} // namespace ms_helloworld 

MicroServerRequestHandlerProvider.h

#ifndef __MICROSERVERREQUESTHANDLERPROVIDER_H__
#define __MICROSERVERREQUESTHANDLERPROVIDER_H__

#include "AbstractRequestHandler.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/ClassLibrary.h"

using Poco::Net::HTTPRequestHandler;

namespace ms_helloworld {
class MicroServerRequestHandlerProvider : public AbstractRequestHandler {
public:
  HTTPRequestHandler* getRequestHandler(std::string uri);
};

} //namespace ms_helloworld
#endif //__MICROSERVERREQUESTHANDLERPROVIDER_H__

MicroServerRequestHandlerProvider.cpp

#include "MicroServerRequestHandlerProvider.h"
#include "MicroServerHelloWorldRequestHandler.h"

namespace ms_helloworld {

HTTPRequestHandler *
MicroServerRequestHandlerProvider::getRequestHandler(std::string uri) {
  if (uri == "/helloworld") {
    return new MicroServerHelloWorldRequestHandler();
  } 
  else {
    return nullptr;
  }
}

} // namespace ms_helloworld

This is my microserver example. When analyzing the microserver in production, you will find that it is very resource efficient. I used Docker to deploy the example. It was very easy to deploy this server with Docker, I will explain what I did to deploy this microserver with Docker in a future post.
I’m happy to receive your opinions and reviews on the microserver and the c++ source code. Anything is very welcome.

Happy Coding Everyone!