Thursday, 1 February 2018

CMake and testing compilation failures

Creating header-only libraries with CMake is easy, one has to declare only INTERFACE parts of the library:
# Nothing to build, so only declare name
add_library(lib_name INTERFACE)

# List all include directories with library headers
target_include_directories(lib_name INTERFACE include)

# Declare dependencies - other header only libraries
target_link_libraries(int_sum INTERFACE Boost::Boost)

Of course, CMakeLists.txt for a library will have install and test steps. Testing compilable code can be done with help of many frameworks (Boost::Test, Google Testlest or Catch), registered by add_test command and executed by ctest tool.

When testing, for each branch in code, one should consider testing it. With header-only libraries, there is a high probability, that there are compile-time conditional constructs (templates, macros). But there are currently no tools to test compile failures. CMake has a try_compile command, but it works during build generation and not during the build itself. So it will not rerun when a developer changes test or library code and executes build and tests. That is why, I have created AddCompileFailureTest - a simple CMake module, which enables testing compilation failures. Using it is straightforward. First, it has to be loaded in the main CMakeLists.txt:
include(AddCompileFailureTest)
Each expected compilation failure should be placed in proper source file within main function. Let us consider a simple sum function:
template<typename T>
inline T sum(const T a, const T b) {
    return a + b;
}
It is obvious, that it will not compile for types, which do not have operator+ defined. The test case is:

#include <sum.h>

class empty {};

int main() {
    empty a;
    empty b;
    empty c =  hlib::sum(a, b);
}

Executing the test is simple and done from tests' CMakeLists.txt:
add_compile_failure_test(
    NAME FailWithoutPlus
    SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/failing_withous_plus.cpp
    INCLUDE_DIRECTORIES ${HLIB_INCLUDES}
)

HLIB_INCLUDES is a variable, which contains include path for a library with the sum function. Presented example is available on GitHub.

Function add_compile_failure_test works by creating a directory in the build tree and creating simple CMake project in it. This project contains a file with test case and CMakeLists.txt, which uses the try_compile command for checking for compilation errors in the test file. If the file can be compiled without errors, a build generation of the test project fails. The function add_compile_failure_test adds to ALL target a target for copying test file to build directory and test, which tries to generate build for the test project. The test fails, when build generation fails, ie. when test file can be compiled without errors.


Friday, 19 January 2018

Modern CMake 101: Introduction


This post is part of a modern CMake tutorial: table of contents.

Modern CMake 101: Introduction

CMake is a cross-platform build system generator, tool to test and package applications. Over the years, it gained attention and now it is widespread. It can even be used in Visual Studio, from 2017 version up, out of the box. Over the years, CMake language has grown and philosophy of writing good build script has changed. There are many tutorials for writing CMake build scripts, but even the official one is outdated and doesn't show modern practices. This post summarizes key facts about creating modern CMake scripts.

The first thing to do is to use recent enough version of CMake, ie. at least 3.0. Without it, one cannot follow best practices. See cmake_minimum_required.

The second thing is to forget about global variable, global compiler flags and thinking about directories. Modern CMake is all about modules and treating targets as objects. Targets have:
  1. named constructors:
  2. properties
  3. member functions
Because targets should be viewed as objects, one should carefully consider which parts of target create its interface and which are internal and used only to build a given target. There are three kinds of target properties:
  1. INTERFACE - properties creating an interface of target and used to build other targets depending on considered one.
  2. PRIVATE - private properties of the target, needed only to build target and should not be propagated to other targets.
  3. PUBLIC - properties which are both: INTERFACE and PRIVATE.

The third thing to keep in mind, when using a modern build tool, is to think about target dependencies and not about compile flags and directories. For expressing, that one target depends on the other, use target_link_libraries. As with all target data, one should carefully consider whether targets dependency should be in interface or not. To properly use targets from a different project, one should always export library interface with help of the install command and its exports mode.

More info can be found: hereherehere and here.

Thursday, 18 January 2018

Windows 10 Pro

I have bought my laptop with Windows 8 Home upgraded to Pro edition, but I haven't got upgrade key from a shop (or it was hidden in the receipt and I forgot where the receipt was). Years have passed, Windows was upgraded to Windows 10 and then Visual Studio stopped working. I have fought bravely, to avoid Windows reinstallation, but MS help said that it was the only way to fix Visual Studio. Reluctantly, I have reinstalled Windows with the help of Media Creation Tool. Then, my Windows was reverted to Home edition and lost the ability to execute virtual machines! Few days with Google lead me to the page with a solution, and I have learned that:
If you performed a clean install using the Media Creation Tool it installs the specific edition based on the OEM Marker in the BIOS. So if the computer originally came preinstalled with Windows 7 Starter, Home Basic, Home Premium or Windows 8/8.1 Core/Single Language Windows 10 Home will be installed.

Workaround:
Upgrade to Windows 10 Pro using the following default product key:

VK7JG-NPHTM-C97JM-9MPGT-3V66T

So, if You bought PC with Home edition of Windows and have upgraded it to Pro version after Windows reinstallation, You should use key VK7JG-NPHTM-C97JM-9MPGT-3V66T!