Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ endif()

option(EQ_BUILD_TESTS "Build the base unit tests" ${eq_is_top_level})
option(EQ_USE_STD_ATOMIC "Build base::Atomic on top of std::atomic" OFF)
# A component build on Windows needs base as one DLL: PE gives every DLL its
# own copy of a static library's globals, so the Option/Feature registries and
# every other base singleton would otherwise split per module.
option(EQ_BASE_SHARED "Build base as a shared library" OFF)

# The base headers use C++23 (deducing this, constexpr extensions).
if(NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 23)
Expand Down Expand Up @@ -46,7 +50,24 @@ foreach(platform ${eq_excluded_platforms})
list(FILTER EQ_BASE_SOURCES EXCLUDE REGEX "/${platform}/")
endforeach()

add_library(eq_base STATIC ${EQ_BASE_SOURCES})
if(EQ_BASE_SHARED)
add_library(eq_base SHARED ${EQ_BASE_SOURCES})
target_compile_definitions(eq_base PUBLIC BUILDING_DLL)
# BASE_EXPORT marks only part of the API; export the rest of the functions
# too. Data crosses the boundary only through BASE_EXPORT (dllimport) or
# accessors.
set_target_properties(eq_base PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
# That property only drives MSVC-style linkers; GNU ld's equivalent. Without
# it, ld exports only the dllexport-marked symbols once any exist.
# --exclude-libs keeps the statically linked runtime (libgcc's unwinder,
# libstdc++) out of the export table, where it would clash with every
# consumer's own copy.
if(MINGW)
target_link_options(eq_base PRIVATE -Wl,--export-all-symbols,--exclude-libs,ALL)
endif()
else()
add_library(eq_base STATIC ${EQ_BASE_SOURCES})
endif()
add_library(equilibrium::base ALIAS eq_base)
target_include_directories(eq_base PUBLIC "${EQ_ROOT}" "${EQ_ROOT}/base")
target_compile_definitions(eq_base PRIVATE
Expand Down
12 changes: 10 additions & 2 deletions base/containers/init_chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@
// visibility makes the root one symbol per process (ELF unique binding), so
// all objects share one chain. An instantiation is only as visible as its
// template argument, so every item type (OptionBase, Feature) carries the same
// annotation. PE has no such binding; there base has to be linked as a DLL for
// the same guarantee.
// annotation.
//
// PE has no such binding: every DLL keeps its own copy of an inline static.
// There the root lives out of line, in TItem::ChainRoot(), which base defines
// once; with base built as a DLL (EQ_BASE_SHARED) every module reaches the
// same one.
#if defined(__GNUC__) || defined(__clang__)
#define BASE_SHARED_REGISTRY __attribute__((visibility("default")))
#else
Expand Down Expand Up @@ -47,10 +51,14 @@ class BASE_SHARED_REGISTRY InitChain {
InitChain(const InitChain&) = delete;
InitChain(InitChain&&) = delete;

#if defined(_WIN32)
STRONG_INLINE static InitChain*& root() noexcept { return TItem::ChainRoot(); }
#else
STRONG_INLINE static InitChain*& root() noexcept {
static InitChain* root{nullptr};
return root;
}
#endif

template <typename TFunctor>
STRONG_INLINE static mem_size VisitAll(const TFunctor functor,
Expand Down
21 changes: 21 additions & 0 deletions base/containers/init_chain_win.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (C) 2026 Vincent Hengel.
// For licensing information see LICENSE at the root of this distribution.
// The registry roots, defined once in base so every DLL shares them
// (base/containers/init_chain.h explains why PE needs this).

#include <base/feature.h>
#include <base/option.h>

namespace base {

InitChain<Feature>*& Feature::ChainRoot() noexcept {
static InitChain<Feature>* root{nullptr};
return root;
}

InitChain<OptionBase>*& OptionBase::ChainRoot() noexcept {
static InitChain<OptionBase>* root{nullptr};
return root;
}

} // namespace base
20 changes: 0 additions & 20 deletions base/debugging/debugging_win.cc

This file was deleted.

5 changes: 5 additions & 0 deletions base/feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ struct BASE_EXPORT BASE_SHARED_REGISTRY Feature : InitChain<Feature> {

inline operator bool() const { return enabled; }

#if defined(_WIN32)
// The chain root, out of line so every DLL shares base's (init_chain.h).
static InitChain<Feature>*& ChainRoot() noexcept;
#endif

const char* name;
Flags flags;
bool enabled;
Expand Down
5 changes: 5 additions & 0 deletions base/option.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ class BASE_EXPORT BASE_SHARED_REGISTRY OptionBase : public InitChain<OptionBase>
// Returns the number of characters written, null terminator excluded.
virtual mem_size FormatValue(char* buffer, mem_size buffer_size) const = 0;

#if defined(_WIN32)
// The chain root, out of line so every DLL shares base's (init_chain.h).
static InitChain<OptionBase>*& ChainRoot() noexcept;
#endif

const char* name() const { return name_; }
const char* env() const { return env_; }
const char* desc() const { return desc_; }
Expand Down
9 changes: 9 additions & 0 deletions base/win/minwin.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
// and skip the hand-rolled forward declarations entirely.
#if defined(__MINGW32__) || defined(__MINGW64__)
#include <windows.h>
// The MSVC path below never includes <windows.h>, so no includer of this
// header sees its macros. Match that for the function-like ones that rename
// ordinary identifiers (a GetProp method becomes GetPropW in the files that
// see this header and stays GetProp in the ones that do not, and the link
// fails). Code that wants the Win32 function calls the W name.
#undef GetProp
#undef SetProp
#undef RemoveProp
#undef MemoryBarrier
#else // MSVC: forward-declare instead of paying for <Windows.h>


Expand Down
Loading