18. Building without CMake¶
EasyLocal Core is header-only and depends only on the standard library: a
compiler, the C++23 flag and one include path build a program. This chapter
gives the whole workflow for a build driven by a makefile or by a single
command — what to do before the first compilation, the makefile of the
tutorial's programs, and the headers and libraries each optional component
needs. CMake stays the supported way to consume the library (the
quick start shows find_package and add_subdirectory);
a makefile is for a course machine, an existing build system, or a program you
compile by hand.
Before you start¶
A compiler. The same ones the quick start
lists: GCC 15 or 16, Clang 22 or 23, AppleClang from Xcode 26. The flags below
are the GCC and Clang spelling; with clang-cl, use /std:c++latest and
/I<dir>.
The headers. Two sources, with the same content: the build generates no header.
-
An installation. Configure EasyLocal once with CMake, then install it where you like:
cmake -S easylocal -B build/release -DCMAKE_BUILD_TYPE=Release cmake --install build/release --prefix ~/.localThe headers land in
~/.local/include/easylocal/, together with the adapters of the optional components the build enabled and with the third-party headers it fetched (see the optional components). -
A source checkout.
git clone https://github.com/iolab-uniud/easylocaland point the include path ateasylocal/include. Nothing is built, and the adapters are there too; only the third-party libraries of the optional components are missing, since a checkout fetches nothing.
The libraries of the optional components, if the program uses one: FTXUI for the TextUI, Crow for the REST service, toml++ for the TOML configuration. They are the only link-time dependency EasyLocal ever has; Core needs none.
No -ffast-math, which lets the compiler assume away the NaN and infinite
values the library checks for, and -O2 or better: the library relies on
inlining, and a search can be an order of magnitude slower with -O0.
One command¶
A single-file program, such as the one of the quick start, needs no makefile:
c++ -std=c++23 -O2 -Wall -I$HOME/.local/include main.cpp -o tsp
./tsp
-I points at the directory that contains easylocal/, so that
#include <easylocal/easylocal.hpp> resolves. There is nothing to link and no
-D to define. Core starts no thread of its own; add -pthread if your
platform requires it for the standard library, or if your program uses
threads.
A makefile¶
The tutorial's programs are built by examples/tutorial/Makefile, which is
the recipe for any EasyLocal program:
# Builds the tutorial's programs without CMake (docs/tutorial/18-building.md).
#
# make EASYLOCAL=/usr/local the programs of Core
# make EASYLOCAL=/usr/local tsp_tui one optional component
#
# EASYLOCAL is an installation prefix or a source checkout: both hold the
# headers under include/. The optional components also need their own library,
# whose prefix is FTXUI, CROW or TOMLPP.
EASYLOCAL ?= /usr/local
CXX ?= c++
CXXFLAGS ?= -std=c++23 -O2 -Wall -Wextra
CPPFLAGS += -I$(EASYLOCAL)/include
FTXUI ?= /usr/local
CROW ?= /usr/local
TOMLPP ?= /usr/local
# The flags of each optional component, derived from its prefix: override a
# prefix, or the flags themselves, on the command line. The third-party
# headers come in with -isystem, which leaves their own warnings out.
TUI_CPPFLAGS ?= -isystem $(FTXUI)/include
TUI_LDLIBS ?= -L$(FTXUI)/lib -lftxui-component -lftxui-dom -lftxui-screen -pthread
REST_CPPFLAGS ?= -isystem $(CROW)/include
REST_LDLIBS ?= -pthread
TOML_CPPFLAGS ?= -isystem $(TOMLPP)/include
# The programs of Core, built by default, and those of the optional components.
PROGRAMS := tsp tsp_cli tsp_checks
OPTIONAL := tsp_tui tsp_rest tsp_toml
all: $(PROGRAMS)
tsp: main.o
tsp_cli: cli_main.o
tsp_checks: checks.o
tsp_tui: tui_main.o
tsp_rest: rest_main.o
tsp_toml: toml_main.o
# A target-specific variable reaches the object files of its program too.
tsp_tui: CPPFLAGS += $(TUI_CPPFLAGS)
tsp_tui: LDLIBS += $(TUI_LDLIBS)
tsp_rest: CPPFLAGS += $(REST_CPPFLAGS)
tsp_rest: LDLIBS += $(REST_LDLIBS)
tsp_toml: CPPFLAGS += $(TOML_CPPFLAGS)
$(PROGRAMS) $(OPTIONAL):
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
%.o: %.cpp tsp.hpp
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
clean:
rm -f $(PROGRAMS) $(OPTIONAL) *.o
.PHONY: all clean
From the example directory, the three programs of Core are one command away:
cd examples/tutorial
make EASYLOCAL=$HOME/.local
./tsp # the searches of chapters 6 and 7
./tsp_cli --help # the command line of chapter 12
./tsp_checks # the component checks of chapter 11
Three things are worth copying into a makefile of your own:
CPPFLAGScarries the include path, and the compilation rule passes it to every object file. Adding a program means adding its.oto the list; the%.o: %.cpp tsp.hpprule rebuilds an object when the header of the problem changes, which a header-only library makes frequent. For more headers, list them there, or generate the dependencies with-MMD -MP.- Every translation unit uses the same flags. EasyLocal's classes are
shared across them, and compiling two of them with different standards or
different
-Ddefinitions breaks the one-definition rule, usually at link time or, worse, not at all. - A target-specific variable (
tsp_tui: CPPFLAGS += ...) reaches the object files built for that program, so the flags of an optional component stay out of the programs that do not use it.
The optional components¶
Each component is a header of easylocal/adapters/ plus the third-party
library it adapts. The component's own header needs no -D definition and no
EasyLocal library:
| Component | Header | Library | Compile | Link |
|---|---|---|---|---|
| Core | <easylocal/easylocal.hpp> |
— | -I<prefix>/include |
— |
| TextUI | <easylocal/adapters/tui.hpp> |
FTXUI ≥ 7 | -isystem <ftxui>/include |
-L<ftxui>/lib -lftxui-component -lftxui-dom -lftxui-screen -pthread |
| REST | <easylocal/adapters/rest.hpp> |
Crow ≥ 1.3, with standalone Asio | -isystem <crow>/include -isystem <asio>/include |
-pthread |
| ConfigTOML | <easylocal/adapters/toml.hpp> |
toml++ ≥ 3.4 | -isystem <toml++>/include |
— |
The three FTXUI libraries are listed in that order: component uses dom,
which uses screen. Crow and toml++ are header-only; Crow includes Asio,
which is header-only as well when it is the standalone version (the one
EasyLocal uses).
Where the libraries come from:
- A package manager. FTXUI, Crow and toml++ are in Homebrew, vcpkg and
several Linux distributions. The prefix is then the package manager's own
(
/opt/homebrewor/usr/localwith Homebrew,/usrwith a distribution package). -
The EasyLocal installation itself. A build configured with
-DEASYLOCAL_FETCH_DEPENDENCIES=ONdownloads what the enabled components need andcmake --installputs it in the same prefix: FTXUI underinclude/ftxui/with its libraries inlib/, Crow underinclude/crow/, and toml++ and Asio as private copies underinclude/easylocal/third_party/. The prefixes of the makefile then all point at the installation:make EASYLOCAL=$HOME/.local FTXUI=$HOME/.local tsp_tui make EASYLOCAL=$HOME/.local \ REST_CPPFLAGS="-isystem $HOME/.local/include -isystem $HOME/.local/include/easylocal/third_party/asio" \ tsp_rest make EASYLOCAL=$HOME/.local \ TOML_CPPFLAGS="-isystem $HOME/.local/include/easylocal/third_party/tomlplusplus" \ tsp_toml -
A source build of the library, installed wherever you like; its prefix is what the makefile wants.
The dependency policy says which versions each component accepts and why. The rest of the framework — tracing, logging, the command line, the tuning files for irace — is Core, so it needs no flag beyond the include path.
What CMake does that a makefile does not¶
A makefile build compiles the same code, with the same behaviour. What you give up is the checking around it:
- the C++23 and compiler-version check of
find_package(EasyLocal), whose message says what is missing instead of a wall of template errors; - the check that a component you ask for is in the installation
(
COMPONENTS TUIfails when the TextUI was not enabled, whereas a missing header is a compiler error); - fetching the third-party libraries, and passing their include paths and libraries to the compiler for you;
- the deployment-target check on macOS, where AppleClang needs Xcode 26 and a
target of 26.0 or later for the floating-point
std::from_charsits standard library uses.
The makefile of this chapter is built and run by the test suite
(easylocal.makefile-build, which installs EasyLocal, compiles the quick
start with one command and builds every program the installation's components
allow), so the flags on this page are the flags that work.