GNUmakefile

From Crypto++ Wiki
Jump to: navigation, search

GNUmakefile is the primary way to configure and build the library and test program from sources on AIX, BSD, Linux, OS X, Solaris, Unix, Cygwin and MinGW. It is used to natively build targets like libcryptopp.a, libcryptopp.so, libcryptopp.dylib and cryptest.exe. Natively means the artifact, like libcryptopp.a or cryptest.exe, is targeted for the host computer or the computer you are building on, like your i686 or x86_64 desktop.

The GNUmakefile is not used to build cross-platform or used with cross-compiles. A different makefile called GNUmakefile-cross is used for targeting an architecture like ARM or MIPS. A different makefile is used because GNUmakefile assumes the target architecture is the same as the host architecture, and that does not hold in a cross-compile.

GNUmakefile-cross does not have a separate page. Once you understand the GNUmakefile, then you will understand GNUmakefile-cross because its a simpler makefile with fewer pieces. Some sections of this page have information dedicated to the difference when cross-compiling. You can read more detailed information about cross-compiling at Android (Command Line), iOS (Command Line), ARM (Command Line) , ARM Embedded (Command Line) and ARM Embedded (Bare Metal).

Using either GNUmakefile or GNUmakefile-cross requires GNU Make 3.81 or higher. Newer platforms, like Debian 7, Fedora 15, OS X 10 and Ubuntu 9 will have it. You will have to install GNU Make on older ones, like Fedora 1. Posix's make is too anemic, so BSD and Solaris users should install the gmake package.

When building the library, everything is driven through the compiler driver, including link. Compiler flags can be specified directly by CXXFLAGS = -foo and the option can be passed to the linker with CXXFLAGS = -Wl,-foo. LDFLAGS is not used in every recipe, so you should try to use CXXFLAGS if possible.

The GNUmakefile is available online from GitHub, and it might be helpful to reference it on occasion. Also note the makefile may have changed since this writing.

A closely related page is config.h, which explains configuration options and preprocessor macros. If a setting is not available in the GNUmakefile, then its likely present in config.h.

config.recommend

The sections below discuss config.recommend. config.recommend is a Crypto++ 5.6.4 and below control that provides an alternate config.h which puts the library in the best state possible at the expense of backwards compatibility. It also required users to do something special. The special step is to copy:

cp config.recommend config.h

At Crypto++ 5.6.5, config.recommend was made the default configuration. That is, config.recommend became config.h. The proactive change was made due to Issue 277 and CVE-2016-7420. Users no longer have to do anything special to get into the recommended state.

If needed, you can get backwards compatibility with config.compat.

Standard Targets

When building the Crypto++ library, the three workhorse targets are static, dynamic and test. static builds the static version of the library, libcryptopp.a. dynamic builds the shared object version of the library, which is libcryptopp.so on Linux and libcryptopp.dylib on OS X. test builds the Crypto++ driver program cryptest.exe.

The standard all target, used if you just type make, enlists static and test.

Autotools and Cmake

Crypto++ primary build system is Make. The community supplies additional systems, like CMake. If you are porting to another build system, like Autotools or Cmake, then you should ensure all, static, dynamic and test work as expected. That's because nearly all the official Crypto++ docs reference them, and its a disservice to as users to learn something different or do something different.

More targets are available, and they are detailed below in Makefile Targets.

Building the Library

The library does a good job of configuring itself out of the box. Usually you can perform the following and things work fine:

wget https://www.cryptopp.com/cryptopp563.zip
unzip -aoq cryptopp563.zip -d cryptopp
cd cryptopp
make

Or using GitHub:

git clone git://github.com/weidai11/cryptopp.git
cd cryptopp
make

A typical output from above on an x86_64 machine looks as follows.

$ make
g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cryptlib.cpp
g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cpu.cpp
...

If you want to perform dead code stripping, then you should issue make lean instead of make. See Makefile Targets below.

If you want to ensure the self tests run after installation, then you need to set CRYPTOPP_DATA_DIR. See Data_Directory below.

CXX and CXXFLAGS

The library does its best to honor your configuration choices. For example:

$ export CXX=clang++
$ make
clang++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cryptlib.cpp
clang++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cpu.cpp
...

And:

$ CXX=clang++ make
clang++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cryptlib.cpp
clang++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cpu.cpp
...

And:

$ export CXXFLAGS="-DNDEBUG -g3 -O2 -fstack-protector-all"
$ make
g++ -DNDEBUG -g3 -O2 -fstack-protector-all -fPIC -march=native -pipe -c cryptlib.cpp
g++ -DNDEBUG -g3 -O2 -fstack-protector-all -fPIC -march=native -pipe -c cpu.cpp
...

Notice the library uses your CXXFLAGS and then adds other flags to it, like -fPIC and -pipe.

If you are building for OS X or iOS under Xcode, then you probably want to ensure -stdlib=libc++ is present because Xcode uses LLVM's runtime (libc++). Xcode does not use GNU's runtime (libstdc++) by default:

$ export CXXFLAGS="-DNDEBUG -g2 -O2 -stdlib=libc++"
$ make
g++ -DNDEBUG -g2 -O2 -stdlib=libc++ -fPIC -march=native -pipe -c cryptlib.cpp
g++ -DNDEBUG -g2 -O2 -stdlib=libc++ -fPIC -march=native -pipe -c cpu.cpp
...

You can instruct make to use only your CXXFLAGS by providing them on the command line as an override. Below is a native compile on a LeMaker Banana Pro, and it avoids -pipe because the device does not have the RAM to process some files in-memory:

bananapi:cryptopp$ make CXXFLAGS="-DNDEBUG -g2 -O3 -fPIC -mtune=cortex-a7"
g++ -DNDEBUG -g2 -O3 -fPIC -mtune=cortex-a7 -c cryptlib.cpp
g++ -DNDEBUG -g2 -O3 -fPIC -mtune=cortex-a7 -c ida.cpp
...

Cross-compiling

If you are cross-compiling, then you ran a setenv-*.sh script for a cross-compile (for example, setenv-iso.sh setenv-iso.sh). Your next step is to use -f GNUmakefile-cross option:

$ . ./setenv-ios.sh 
Configuring for Device (ARMv7)
XCODE_SDK: iPhoneOS8.2.sdk
...

$ make -f GNUmakefile-cross 
clang++ -DNDEBUG -g2 -Os -fPIC -pipe -Wall -arch armv7 -isysroot /Applications/Xcode.app/
 Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.2.sdk
 -stdlib=libc++ -c cryptlib.cpp
clang++ -DNDEBUG -g2 -Os -fPIC -pipe -Wall -arch armv7 -isysroot /Applications/Xcode.app/
 Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.2.sdk
 -stdlib=libc++ -c cpu.cpp
...

Testing the Library

You should always test the library after you build it. The library can have trouble on a number of compilers, especially when the optimizer starts inlining functions. Optimizations and bad code generation have been the cause of a number of Crypto++ bug reports. See, for example, Crash on Cygwin-x64 with -DDEBUG -Os and Hang on Debian ARM64 QEMU Chroot.

You test the library using cryptest.exe using both the Validation Suite (v option) and the Test Vectors (tv option). Here's how to invoke it.

# Validation Suite
$ ./cryptest.exe v
...
All tests passed!
Test ended at Sun Jan  3 08:50:06 2016

And:

# Test Vectors
$ ./cryptest.exe tv all
...
Tests complete. Total tests = 4111. Failed tests = 0.

If you want a more comprehensive or thorough testing of the library under a compiler on a platform, then run the cryptest.sh script. The cryptest.sh script repeatedly builds the library and executes the self tests under different configurations. More details can be found at Release Process.

Cross-compiling

If you are cross-compiling, then you have to manually test the build on a device. Under Android, you have to use adb to push the cryptest.exe program, shared objects, Test Data and Test Vectors to the device. With iOS, you have to use SSH to copy cryptest.exe program, Test Data and Test Vectors to the device.

Once on the device, you can run things like a traditional desktop. Since this is a cross-compile, you cannot use cryptest.sh to drive things because the script is designed to run natively.

Installing the Library

Use the make install recipe to install the library. The recipe will install the header and libraries based on PREFIX. The library will also install test data files when the recipe is executed.

Care must be taken to ensure the self tests run after installation if its important to you. Care must be taken because Crypto++ does not use a configuration file per se (like, say openssl.cnf), and the paths need to be wired in during compile. The data directory is wired into the sources via the C/C++ macro CRYPTOPP_DATA_DIR, and more information is available in Data Directory below. If you don't care about it, then you don't need to worry about it.

Components

The table below provides the components and their installation locations. Its based on an install or root path of PREFIX=/usr/local.

Component and Locations
Component Location Exemplary Path Comment
cryptest.exe $PREFIX/bin /usr/local/bin cryptest.exe is only installed if it was built.
libcryptopp.a $PREFIX/lib /usr/local/lib libcryptopp.a is only installed if it was built. Its always built when cryptest.exe is a target.
libcryptopp.so $PREFIX/lib /usr/local/lib libcryptopp.so is used on BSD, Linux, Unix and other compatibles. It is only installed if it was built. If SONAMES are in effect, then libcryptopp.so is a soft link and the actual library will be libcryptopp.so.5.6.3 (or similar).
libcryptopp.dylib $PREFIX/lib /usr/local/lib libcryptopp.dylib is used on OS X. It is only installed if it was built.
Headers $PREFIX/include/cryptopp /usr/local/include/cryptopp Header files are always installed.
Test Data $PREFIX/share/cryptopp/TestData /usr/local/share/cryptopp/TestData Test Data are only installed if cryptest.exe was built.
Test Vectors $PREFIX/share/cryptopp/TestVectors /usr/local/share/cryptopp/TestVectors Test Vectors are only installed if cryptest.exe was built.

Data Directory

When the library was tested in Testing the Library, the test data and test vectors were in the build directory. It worked fine for building and testing in place. However, you must perform additional steps if you want cryptest.exe to run after installations because cryptest.exe will be in a bin/ directory, while the test data and test vectors will be in a share/ directory.

Building and installing the library is a three-step process when using CRYPTOPP_DATA_DIR. First, you need to determine the root directory for the install. By default the root directory is /usr/local/. Second, you need to pass the data directory via CXXFLAGS (or export CRYPTOPP_DATA_DIR or change it in config.h). Third, you need to pass the install directory during the make install.

In the example that follows, the install will occur in /tmp/local/ to test things. First, start by cleaning the previous build.

# Clean previous stuff
$ make clean
...

Second, set your flags to suit your taste. There are three note well's in this step:

  • you must specify the actual data directory, and not the install directory
  • you must include the trailing slash because simple string concatenation is used
  • the string must have quotes, so the quote is escaped (\"), and a single quote is added to guard the escaped quote
# Data directory, not install directory
$ export CXXFLAGS="-DNDEBUG -g2 -O2 -DCRYPTOPP_DATA_DIR='\"/tmp/local/share/cryptopp/\"'"
$ make
g++ -DNDEBUG -g2 -O2 -DCRYPTOPP_DATA_DIR='"/tmp/local/share/cryptopp/"' -fPIC -march=native -pipe -c cryptlib.cpp
g++ -DNDEBUG -g2 -O2 -DCRYPTOPP_DATA_DIR='"/tmp/local/share/cryptopp/"' -fPIC -march=native -pipe -c cpu.cpp
...

Third, perform the install using PREFIX. This is the same prefix used with GNU make variables, but its capitalized. It also lacks the share/ that CRYPTOPP_DATA_DIR included.

# Install directory, not the data directory
$ make install PREFIX=/tmp/local/
mkdir -p /tmp/local/include/cryptopp
cp *.h /tmp/local/include/cryptopp
chmod 755 /tmp/local/include/cryptopp
chmod 644 /tmp/local/include/cryptopp/*.h
mkdir -p /tmp/local/lib
cp libcryptopp.a /tmp/local/lib
chmod 644 /tmp/local/lib/libcryptopp.a
mkdir -p /tmp/local/bin
cp cryptest.exe /tmp/local/bin
chmod 755 /tmp/local/bin/cryptest.exe
mkdir -p /tmp/local/share/cryptopp
cp -r TestData /tmp/local/share/cryptopp
cp -r TestVectors /tmp/local/share/cryptopp
chmod 755 /tmp/local/share/cryptopp
chmod 755 /tmp/local/share/cryptopp/TestData
chmod 755 /tmp/local/share/cryptopp/TestVectors
chmod 644 /tmp/local/share/cryptopp/TestData/*.dat
chmod 644 /tmp/local/share/cryptopp/TestVectors/*.txt

After installing the library, everything works as expected:

$ cd /tmp/local/bin/
$ ./cryptest.exe v
Using seed: 1451851092      

Testing Settings...
...

All tests passed!
Test ended at Sun Jan  3 08:50:06 2016

If desired, you can also export the CRYPTOPP_DATA_DIR (and not fiddle with CXXFLAGS). In this case, the macro will only be used in source files that are sensitive to it. The source files that use the macro are:

  • test.cpp, datatest.cpp
  • bench1.cpp, bench2.cpp
  • validat1.cpp, validat2.cpp, validat3.cpp
$ export CRYPTOPP_DATA_DIR=\"/tmp/local/share/cryptopp/\"
$ make
g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cryptlib.cpp
g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cpu.cpp
...

g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -DCRYPTOPP_DATA_DIR=\""/tmp/local/share/cryptopp/"\" -c bench.cpp
g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -DCRYPTOPP_DATA_DIR=\""/tmp/local/share/cryptopp/"\" -c bench2.cpp
...

$ make install PREFIX=/tmp/local/
...

Cross-compiling

If you are cross-compiling, then you have to manually install the artifacts on a device. Under Android, you have to use adb to push the cryptest.exe program, shared objects, Test Data and Test Vectors to the device. With iOS, you have to use SSH to copy cryptest.exe program, Test Data and Test Vectors to the device.

Creating Programs

The last step in using the library is creating programs and apps. The recommendations for building the Crypto++ 5.6.4 and below library and applications are:

  • copy config.recommend to config.h
  • build the library and program with the same compiler, options and defines
  • define NDEBUG for production apps or programs

At Crypto++ 5.6.5, config.recommend was made the default configuration. Nothing special is required to put the library in a recommended configuration. The recommendations for building the Crypto++ 5.6.5 and above library and applications are:

  • build the library and program with the same compiler, options and defines

Compilers and C++ Runtimes

Crypto++ builds under a number of different compilers, including GNU's GCC, LLVM's Clang and Intel's ICC. If a distribution provides Crypto++ built with GCC, then you should use GCC for your program. Conversely, if a distribution provides Crypto++ built with Clang, then you should use Clang for your program. You will encounter both on Linux with Apt, Yum, Emerge, etc; and on OS X with MacPorts, HomeBrew, etc.

If you mix and match compilers like GCC and Clang, then you will probably encounter undefined symbols because different compilers use different C++ runtimes by default. Examples of unexplained missing symbols include the following. It happens all the time because of LLVM's inlined namespace, so you should be aware of it (and its not limited to Crypto++):

GCC 5.1 and the C++11 ABI change created some additional problems for Clang. In this situation, the library is compiled by a distro with GCC, and the user attempts to compile and link a program with Clang:

You can sometimes overcome the missing symbols using -stdlib=libstdc++ for GNU's C++ runtime, and -stdlib=libc++ for LLVM's C++ runtime. You add the -stdlib=... option to CXXFLAGS. But in the end, its probably best to build the library and program with the same compiler, options and defines. Things will usually "just work" for you when using the the same compiler, options and defines.

Compiling and Linking

A final problem in building your application is mixing and matching version of the library. Here, the mixing and matching occurs when the distribution provides the library and you build the library from sources.

Good: the following will compile and link against the distribution's version of the Crypto++ library:

$ g++ -DNDEBUG -g2 -O2 test.cxx -o test.exe -lcryptopp

Good: the following will compile and link against your copy of the library. Your copy of the library is located in ./cryptopp.

$ ls
cryptopp        test.cxx

$ g++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe ./cryptopp/libcryptopp.a

Bad: the following will create problems because it uses the distro's copy of library headers, but links against your copy of the library:

$ cd cryptopp
$ g++ -DNDEBUG -g2 -O2 test.cxx -o test.exe ./libcryptopp.a

Bad: the following will create problems because you compile and link against your copy of the library, but at runtime it links to the distro's copy of the library:

$ ls
cryptopp        test.cxx

$ g++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe -L ./cryptopp -lcryptopp

Good: to avoid the problems with runtime linking, use the static archive. This is what the cryptest.exe program does to avoid the problem:

$ g++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe ./cryptopp/libcryptopp.a

Good: or, use LD_LIBRARY_PATH:

$ g++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe -L ./cryptopp -lcryptopp
$ LD_LIBRARY_PATH="./cryptopp:$LD_LIBRARY_PATH" ./test.exe

Good: on OS X, which probably uses Clang by default, you would use clang++ and DYLD_LIBRARY_PATH:

$ clang++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe -L ./cryptopp -lcryptopp
$ DYLD_LIBRARY_PATH="./cryptopp:$DYLD_LIBRARY_PATH" ./test.exe

Good: if you installed the library in /usr/local, then the following will work with static linking. Again, this is what the cryptest.exe program does to avoid the problem:

$ g++ -DNDEBUG -g2 -O2 -I /usr/local/include test.cxx -o test.exe /usr/local/lib/libcryptopp.a

Good: if you installed the library in /usr/local, then the following will work with dynamic linking:

$ g++ -DNDEBUG -g2 -O2 -I /usr/local/include test.cxx -o test.exe -L /usr/local/lib -lcryptopp
$ LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH" ./test.exe

Good: and the same holds on OS X with dynamic linking:

$ clang++ -DNDEBUG -g2 -O2 -I /usr/local/include test.cxx -o test.exe -L /usr/local/lib -lcryptopp
$ DYLD_LIBRARY_PATH="/usr/local/lib:$DYLD_LIBRARY_PATH" ./test.exe

Running Benchmarks

The makefile has a builtin rule to run the benchmarks. You should supply the cpu frequency when benchmarking, and you can do it with CRYPTOPP_CPU_FREQ=3.0 (3.0 means 3 GHz,; change it to suit your taste). After you make a HTML file with the results is available at benchmarks.html.

As of Crypto++ 6.0, the benchmark program produces a complete HTML5 page. On Unix and Linux you can view the results and write the file with:

$ make
$ CRYPTOPP_CPU_FREQ=3.0 make benchmarks | tee benchmarks.html

The naïve way to benchmark is to make with defaults:

$ make
$ CRYPTOPP_CPU_FREQ=3.0 make benchmarks

The benchmark recipe adds the b subcommand, a time of 2 seconds for each test, and passes 3.0 as the cpu freqency to cryptest.exe. You can do the same with:

$ make
$ ./cryptest.exe b 2 3.0

Optimizations

The method above is naïve because it allows unaligned data access (sometimes slower), it uses the default optimization level (-O2) and the default CPU speed of 0.5 GHz (which is probably notcorrect!). A better way to benchmark is:

# Turn up optimizations
export CXXFLAGS="-DNDEBUG -g2 -O3"

# Build it
make -j 4

# Execute benchmarks with CPU speed:
CRYPTOPP_CPU_FREQ=3.0 make benchmarks

Makefile Layout

The makefile is logically laid out in six sections: Variables, X86/X32/X64 Options, Other Platform Options, Common Options, Sources and Objects and Targets/Recipes.

The makefile has section headers to delineate among the sections as depicted below.

==============================
          Variables
==============================
     X86/X32/X64 options
==============================
    Other Platform options
==============================
        Common options
==============================
     Sources and objects
==============================
       Targets/Recipes
==============================

For example, the Variables section is demarcated with:

###########################################################
#####                General Variables                #####
###########################################################

Makefile Variables

The makefile uses a number of variables, including CXX, CXXFLAGS, IS_X86, IS_X86_64, IS_LINUX, IS_DARWIN, GCC_COMPILER and CLANG_COMPILER, PREFIX and DESTDIR. A comprehensive list is not available, but they are easy to find by scanning the GNUmakefile.

Variables can show up at any place in the makefile, and they often do. For example, LIB_MAJOR, LIB_MINOR and LIB_PATCH are in the middle of the makefile and used to build the string 5.6.3, 5.7.0, etc. The string is built by parsing config.h and CRYPTOPP_VERSION on the fly.

The most important variables are probably CXX and CXXFLAGS because they drive the compile and link process. The first line of makefile begins with respecting a user's choice for CXXFLAGS. You can specify anything you want CXXFLAGS, and the library will attempt to honor it.

# Base CXXFLAGS used if the user did not specify them
CXXFLAGS ?= -DNDEBUG -g2 -O2

Around line 15, the makefile sets up common commands in variables. Again, the makefile attempts to respect your choice:

AR ?= ar
ARFLAGS ?= -cr
RANLIB ?= ranlib

CP ?= cp
CHMOD ?= chmod
MKDIR ?= mkdir
EGREP ?= egrep
...

After the basic commands are determined, the makefile proceeds to detect the host's platform around line 25. This is the primary reason there is a separate GNUmakefile-cross:

UNAME := $(shell uname)
IS_X86 := $(shell uname -m | $(EGREP) -i -c "i.86|x86|i86|amd64")
IS_X86_64 := $(shell uname -m | $(EGREP) -i -c "(_64|d64)")
IS_AARCH64 := $(shell uname -m | $(EGREP) -i -c "aarch64")

IS_SUN := $(shell uname | $(EGREP) -i -c "SunOS")
IS_LINUX := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "Linux")
IS_MINGW := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "MinGW")
IS_CYGWIN := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "Cygwin")
IS_DARWIN := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "Darwin")

Sometimes, the makefile does not honor your choice (but its not out of disrespect). For example, if Apple's OS X is detected, then AR is changed to libtool and ARFLAGS is changed to static -o. If want to override the defaults, you will have to tune it by hand:

IS_DARWIN := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "Darwin")
...

ifneq ($(IS_DARWIN),0)
  CXX ?= c++
  AR = libtool
  ARFLAGS = -static -o
endif

There are some makefile variables that allow finer grain control over installation directories. Recall from Installing the Library you issue make install PREFIX=/usr/local. The makefile derives other locations from PREFIX, and you are allowed to override each one of them.

ifeq ($(PREFIX),)
PREFIX = /usr/local
endif

# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html
ifeq ($(DATADIR),)
DATADIR := $(PREFIX)/share
endif
ifeq ($(LIBDIR),)
LIBDIR := $(PREFIX)/lib
endif
ifeq ($(BINDIR),)
BINDIR := $(PREFIX)/bin
endif
ifeq ($(INCLUDEDIR),)
INCLUDEDIR := $(PREFIX)/include
endif

The above means that you can install the libraries into, say, lib64 by explicitly setting it to a value:

make install PREFIX=/usr/local LIBDIR=/usr/local/lib64

Again, these are GNU make variables, but they are capitalized. The reason for capital letters is historically, Crypto++ used them, so the tradition was unchanged. When the library added DESTDIR, DATADIR, LIBDIR and friends, it favored consistency so the rule to remember is: always use capitol letters.

X86/X32/X64 Options

The X86/X32/X64 options are a big block that simply tweaks variables used in recipes based on command goals. For example, if you issue make ubsan to build and test under an Clang or GCC's Undefined Behavior Sanitizer, then around line 200, this is activated:

# Undefined Behavior Sanitizer (UBsan) testing. There's no sense in
#   allowing unaligned data access. There will too many findings.
ifeq ($(findstring ubsan,$(MAKECMDGOALS)),ubsan)
ifeq ($(findstring -fsanitize=undefined,$(CXXFLAGS)),)
CXXFLAGS += -fsanitize=undefined
endif # CXXFLAGS
ifeq ($(findstring -DCRYPTOPP_NO_UNALIGNED_DATA_ACCESS,$(CXXFLAGS)),)
CXXFLAGS += -DCRYPTOPP_NO_UNALIGNED_DATA_ACCESS
endif # CXXFLAGS
endif # UBsan

The above block ensures the proper flags are set. It still honors your CXXFLAGS, but it supplements them as required so you get what you ask for. If it was not done, then the compile or test could fails, and that could lead to frustration and waste more time than it deserved.

There are lots of supplements like above, and they include:

  • Adding -fPIC for x86_64 targets
  • Adding -march=native so AES-NI, RDRAND and RDSEED are available
  • Adding -DCRYPTOPP_NO_UNALIGNED_DATA_ACCESS for -O3 builds
  • Disabling ASM with -DCRYPTOPP_DISABLE_ASM due to down-level assemblers
  • Disabling SSE3 with -DCRYPTOPP_DISABLE_SSSE3 due to down-level assemblers
  • Disabling AES-NI with -DCRYPTOPP_DISABLE_AESNI due to down-level assemblers

Other Platform Options

Other platform options is a small section, and it includes building PowerPC, ARM and MIPS natively, like you usually build i686 or x86_64. What differentiates this case from GNUmakefile-cross uses is you are targeting the host and the host is ARM or MIPS. You will encounter this when, say, SSH'ing into a BeagleBone Black and running the compiler from the BeagleBone host.

In this case, the platform specific option is simply to add -fPIC to avoid a link error due to missing relocation R_ARM_THM_MOVW_ABS_NC against `a local symbol`:

# Add PIC
ifeq ($(findstring -fPIC,$(CXXFLAGS)),)
  CXXFLAGS += -fPIC
endif

Common Options

There are some tweaks and supplements that are common to all platform. They follow the specific platform options and include:

  • Adding -pthread and -lgomp as required
  • Adding -fsanitize=undefined for ubsan target
  • Adding -fsanitize=address for asan target
  • Adding -Wl,-fuse-ld=gold if LD is ld.gold
  • Adding -D_GLIBCXX_DEBUG and -D_GLIBCXX_CONCEPT_CHECKS for libstdc++ debug builds

Note well: common options include -pipe. The -pipe option uses memory and avoids temporary files during compilation. However, resource constrained devices and boards sometimes lack enough memory to keep things in-memory. If you get an unexplained GCC compiler panic, like described at Linaro Bug 1997: GCC crash using -mtune=cortex-a53 with message "Warning: end of file in string; '"' inserted", then you should remove the -pipe option.

Sources and Objects

The sources and objects list simply builds the list of source and object files used byte the targets and recipes. The order of the source files and objects in the list is important to help manage C++ static initialization problems, and that's why cryptlib.cpp is at the head of the list:

# List cryptlib.cpp first and cpu.cpp second in an attempt to tame C++ static initialization problems.
#  The issue spills into POD data types of cpu.cpp due to the storage class of the bools, so cpu.cpp
#  is the second candidate for explicit initialization order.
SRCS := cryptlib.cpp cpu.cpp $(filter-out cryptlib.cpp cpu.cpp pch.cpp simple.cpp winpipes.cpp cryptlib_bds.cpp,$(wildcard *.cpp))

# No need for CPU or RDRAND on non-X86 systems. X32 is represented with X64.
ifeq ($(IS_X86)$(IS_X86_64),00)
  SRCS := $(filter-out cpu.cpp rdrand.cpp, $(SRCS))
endif

ifneq ($(IS_MINGW),0)
SRCS += winpipes.cpp
endif

...

Makefile Targets

Targets and their recipes are what most people think of when discussing a makefile. Generally speaking, Crypto++ tries to follow GNU's standard targets for users.

Sometimes a recipe can be called by a few names. For example, GNU calls out the target check, while the makefile responds to both check and test. Below is a list of the targets with comments and notes.

Makefile Targets
Target Comment
all default target, builds libcryptopp.a and cryptest.exe.
static, libcryptopp.a builds the static archive libcryptopp.a.
shared, dynamic, libcryptopp.so builds the shared object libcryptopp.so on BSD, Unix and Linux.
shared, dynamic, libcryptopp.dylib builds the dynamic library libcryptopp.dylib on OS X.
check, test builds libcryptopp.a and cryptest.exe, and then executes the validation suite with cryptest.exe v.
clean cleans the Crypto++ directory of most intermediate objects and output artifacts; for example, *.o, libcryptopp.a, libcryptopp.so and libcryptopp.dylib.
distclean cleans the Crypto++ directory of nearly all things that were created and not unzipped; for example, it removes intermediate files, output artifacts, the documentation directory (ref563) and dependencies (GNUmakefile.deps).
install installs the output artifacts into their customary location using PREFIX; for example, cryptest.exe is placed in $PREFIX/bin and libcryptopp.a is placed in $PREFIX/lib. Header files are placed in $PREFIX/include/cryptopp. This recipe also sets permissions on directories and artifacts to either 0755 (read and execute) or 0644 (read-only).
remove, uninstall removes output artifacts from their installed location using PREFIX.
zip, dist builds the distribution ZIP file (not a TAR file) using Filelist.txt. Prior to zip'ing, the convert recipe is run.
iso builds the ZIP distribution, and then packages it in a ISO file. This is used to transfer sources to old VMs, like Fedora 1. The old VMs don't have network card drivers, but they do have CDROMs. This is primarily used for Testing and QA.
deps creates the makefile dependencies in GNUmakefile.deps. If you don't issue make deps, then they are not available.
bench, benchmark, benchmarks executes the test program with cryptest.exe b 3 2.4 using max duration of 3 seconds and CPU speed of 2.4 GHz (2.4+1e9)
lean builds libcryptopp.a, libcryptopp.so (libcryptopp.dylib on OS X) and cryptest.exe with -ffunction-sections and -fdata-sections to allow dead code stripping. Applications will need to link with -Wl,--gc-sections (-Wl,-dead_strip on OS X) to perform the stripping.
asan builds libcryptopp.a and cryptest.exe with Address Sanitizer support. This is primarily used for Testing and QA.
ubsan builds libcryptopp.a and cryptest.exe with Undefined Behavior Sanitizer support. This is primarily used for Testing and QA.
sources lists the source files used for (1) libcryptopp.a and (2) cryptest.exe. The file list can then be used for Autotools, Cmake, etc.
docs, html builds the HTML-based Doxygen documentation. The output directory is based on the library version; for example, ref563, ref57, and so on. Its named like this because the web server uses it that way.
convert converts text files to CRLF endings, and converts Unix-compatible scripts ot LF endings. That's simply what the project uses as its standard, and its been that way for about 20 years.

Makefile Recipes

The makefile has a standard recipe it uses for object files and its shown below. It is at the end of the makefile and is a "catch all" rule. It will be used if a more specific rule is not found to build an object file.

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

In addition, workarounds for some platforms are applied via recipes. For example, the following is used for Macport GCC compilers because they lack init_priority. It proceeds the standard object file recipe so make will use it when activated:

ifeq ($(GCC_COMPILER)$(MACPORTS_COMPILER),11)
ifeq ($(findstring -DMACPORTS_GCC_COMPILER,$(CXXFLAGS)),)
cryptlib.o:
       $(CXX) $(CXXFLAGS) -DMACPORTS_GCC_COMPILER=1 -c cryptlib.cpp
cpu.o:
       $(CXX) $(CXXFLAGS) -DMACPORTS_GCC_COMPILER=1 -c cpu.cpp
endif
endif

...
%.o : %.cpp
       $(CXX) $(CXXFLAGS) -c $

The recipe to build the static archive is as follows. Making $(LIBOBJS) a order-relative prerequisite means the library is only made if needed, and not every time make is invoked.

libcryptopp.a: $(LIBOBJS) | public_service
       $(AR) $(ARFLAGS) $@ $(LIBOBJS)
       $(RANLIB) $@

The recipe to build the shared object on BSD, Linux and Unix compatibles is as follows. If versioning is being used and HAS_SOLIB_VERSION is in effect, then the library will be named like libcryptopp.so.5.6.3 and libcryptopp.so will be soft linked to it. Otherwise, the artifact will be named libcryptopp.so.

libcryptopp.so$(SOLIB_VERSION_SUFFIX): $(LIBOBJS) | public_service
       $(CXX) -shared $(SOLIB_FLAGS) -o $@ $(CXXFLAGS) $(GOLD_OPTION) $(LIBOBJS) $(LDLIBS)
ifeq ($(HAS_SOLIB_VERSION),1)
       -$(LN) libcryptopp.so$(SOLIB_VERSION_SUFFIX) libcryptopp.so
       -$(LN) libcryptopp.so$(SOLIB_VERSION_SUFFIX) libcryptopp.so$(SOLIB_COMPAT_SUFFIX)
endif

The recipe to build the dynamic library on OS X is as follows.

libcryptopp.dylib: $(LIBOBJS)
       $(CXX) -dynamiclib -o $@ $(CXXFLAGS) -install_name "$@" -current_version "$(LIB_MAJOR).$(LIB_MINOR).$(LIB_PATCH)" \
              -compatibility_version "$(LIB_MAJOR).$(LIB_MINOR)" $(LIBOBJS)

The recipe to build the cryptest program is as follows. Notice it uses the compiler driver to perform the link:

cryptest.exe: libcryptopp.a $(TESTOBJS) | public_service
       $(CXX) -o $@ $(CXXFLAGS) $(TESTOBJS) ./libcryptopp.a $(LDFLAGS) $(GOLD_OPTION) $(LDLIBS)

There's one final recipe that is intended to help Crypto++ package maintainers, and it is the use of DESTDIR for the install rule. All installation recipes use DESTDIR:

.PHONY: install
install:
        $(MKDIR) -p $(DESTDIR)$(INCLUDEDIR)/cryptopp
        $(CP) *.h $(DESTDIR)$(INCLUDEDIR)/cryptopp
        -$(CHMOD) 0755 $(DESTDIR)$(INCLUDEDIR)/cryptopp
        -$(CHMOD) 0644 $(DESTDIR)$(INCLUDEDIR)/cryptopp/*.h
ifneq ($(wildcard libcryptopp.a),)
        $(MKDIR) -p $(DESTDIR)$(LIBDIR)
        $(CP) libcryptopp.a $(DESTDIR)$(LIBDIR)
        -$(CHMOD) 0644 $(DESTDIR)$(LIBDIR)/libcryptopp.a
endif
ifneq ($(wildcard cryptest.exe),)
        $(MKDIR) -p $(DESTDIR)$(BINDIR)
        $(CP) cryptest.exe $(DESTDIR)$(BINDIR)
        -$(CHMOD) 0755 $(DESTDIR)$(BINDIR)/cryptest.exe
        $(MKDIR) -p $(DESTDIR)$(DATADIR)/cryptopp
        $(CP) -r TestData $(DESTDIR)$(DATADIR)/cryptopp
        $(CP) -r TestVectors $(DESTDIR)$(DATADIR)/cryptopp
        -$(CHMOD) 0755 $(DESTDIR)$(DATADIR)/cryptopp
        -$(CHMOD) 0755 $(DESTDIR)$(DATADIR)/cryptopp/TestData
        -$(CHMOD) 0755 $(DESTDIR)$(DATADIR)/cryptopp/TestVectors
        -$(CHMOD) 0644 $(DESTDIR)$(DATADIR)/cryptopp/TestData/*.dat
        -$(CHMOD) 0644 $(DESTDIR)$(DATADIR)/cryptopp/TestVectors/*.txt
endif
        ...

It used to work!!!

If Crypto++ used to work for you under Crypto++ 5.6.2, but fails to work as expected under 5.6.3 or 5.6.4, then its probably due to GNUmakefile changes or changes in code. In this case, you can use Git to go back in time. The example below uses the Crypto++ 5.6.2 makefile to build the latest sources.

$ git clone https://github.com/weidai11/cryptopp cryptopp-past-and-present
Cloning into 'cryptopp-past-and-present'...
...

$ cd cryptopp-past-and-present
$ git checkout CRYPTOPP_5_6_2
Note: checking out 'CRYPTOPP_5_6_2'. You are in 'detached HEAD' state...
...

$ cp GNUmakefile GNUmakefile-5.6.2
$ git checkout CRYPTOPP_5_6_4        # or 'checkout master'
$ cp bench1.cpp bench.cpp            # account for the bench.cpp -> bench1.cpp rename

$ make -f GNUmakefile-5.6.2          # use the old makefile
c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c shacal2.cpp
c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c md5.cpp
c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c shark.cpp
c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c zinflate.cpp
...

We use the technique above often to determine if we introduced a break.

Downloads

cryptest-sh.zip - archive with script to test Crypto++ using various configurations. The configurations include Debug, Release, -O1, -O2, -O3, -Os, C++03, C++11, C++14, C++17, GNU's libstdc++, LLVM's libc++, Undefined Behavior sanitizer, Address sanitizer and Valgrind.