Developer Guide
Building from Source
To compile NeuG from source, certain dependencies and tools must be installed.
As nearlly all dependencies are also included as third-party libraries in the NeuG repository, you could build NeuG locally by installing only a few essential packages.
On Ubuntu
sudo apt-get update
sudo apt-get install -y build-essential cmake git python3-dev python3-pip g++ make libssl-dev opensslOn macOS
brew update
brew install cmake git python3 openssl@3
# No need to install g++ since Apple Clang is also supported.On CentOS7
# Update yum repository to use vault.centos.org for CentOS 7, as the main mirrors are no longer available.
sed -i "s/mirror.centos.org/vault.centos.org/g" /etc/yum.repos.d/*.repo && \
sed -i "s/^#.*baseurl=http/baseurl=http/g" /etc/yum.repos.d/*.repo && \
sed -i "s/^mirrorlist=http/#mirrorlist=http/g" /etc/yum.repos.d/*.repo
sudo yum -y install centos-release-scl
sed -i "s/mirror.centos.org/vault.centos.org/g" /etc/yum.repos.d/*.repo && \
sed -i "s/^#.*baseurl=http/baseurl=http/g" /etc/yum.repos.d/*.repo && \
sed -i "s/^mirrorlist=http/#mirrorlist=http/g" /etc/yum.repos.d/*.repo
sudo yum -y install epel-release
sudo yum -y groupinstall "Development Tools"
sudo yum -y install git python3 python3-pip make cmake3 openssl openssl-devel
sudo ln -sf /usr/bin/cmake3 /usr/local/bin/cmake
# Install newer gcc/g++ via devtoolset-10
sudo yum -y install devtoolset-10
scl enable devtoolset-10 bashOn CentOS8/CentOS Stream 8
sudo dnf -y install epel-release dnf-plugins-core
# Enable PowerTools (called PowerTools in CentOS 8; usually powertools in Stream 8)
sudo dnf config-manager --set-enabled powertools || sudo dnf config-manager --set-enabled PowerTools
sudo dnf -y groupinstall "Development Tools"
sudo dnf -y install git python3 python3-pip cmake gcc-c++ makeBuilding NeuG with Python
With the environment ready, you can proceed to build NeuG with Python and use Python client.
Build model: NeuG uses a single root build tree at <repo>/build/. The
core engine is compiled once into a shared library libneug.{dylib,so}, and
each language binding (currently Python; future Node/Rust) builds its own .so
that dynamically links against the same core library. There is no separate
per-binding build of the core.
<repo>/build/
├── src/libneug.{dylib,so} # core, built once
└── tools/python_bind/neug_py_bind*.so # binding, links to coreFor Development Purposes
To configure the root build and compile the Python binding in one shot:
make python-devThis bootstraps the root build (cmake -S . -B build -DBUILD_PYTHON=ON),
compiles the core, and builds the neug_py_bind target. Subsequent runs only
rebuild what changed. You can then import neug directly:
cd tools/python_bind
python3
>>> import neugThe loader auto-discovers neug_py_bind*.so in <repo>/build/tools/python_bind/
— no sys.path mangling needed. Point at an alternate build tree with
NEUG_BUILD_DIR=/path/to/build.
For the tightest incremental loop, run make dev directly from
tools/python_bind/. It auto-bootstraps if the root build is missing or
wasn’t configured with -DBUILD_PYTHON=ON, so you can start with it from a
fresh checkout too.
Building the Wheel Package
make python-wheelThe wheel is written to tools/python_bind/dist/. It bundles both
neug_py_bind*.so and libneug.{dylib,so} inside the neug package — they
find each other at runtime via @loader_path (macOS) / $ORIGIN (Linux)
RPATH, so the wheel is fully self-contained.
setup.py consumes the root build artifacts. In CI (cibuildwheel containers
with no pre-existing build tree), it falls back to running cmake configure +
--target neug_py_bind in the container.
Building C++ Libraries and Executables Only
To build only the C++ libraries and executables without the Python bindings:
make cpp-build # configures with -DBUILD_PYTHON=OFF, then buildsBoth targets accept BUILD_TYPE (default Release) and EXTRA_CMAKE_FLAGS:
BUILD_TYPE=Debug make cpp-build
EXTRA_CMAKE_FLAGS="-DBUILD_HTTP_SERVER=ON -DWITH_MIMALLOC=ON" make cpp-buildEquivalent raw cmake form:
cmake -S . -B build -DBUILD_PYTHON=OFF
cmake --build build -j
cmake --install build # optional: install to the systemCheck CMakeLists.txt for more CMake options.
Build Options
You can customize the build process by specifying the following environment variables to set different options:
export BUILD_EXECUTABLES=ON/OFF # Toggle building utility executables
export BUILD_HTTP_SERVER=ON/OFF # Enable or disable HTTP server support in NeuG
export WITH_MIMALLOC=ON/OFF # Decide whether to use mimalloc instead of the default malloc from glibc
export ENABLE_BACKTRACES=ON/OFF # Link NeuG libraries with cpptrace for detailed stack trace on exceptions
export BUILD_TYPE=DEBUG/RELEASE # Set the CMake build type
export BUILD_TEST=ON/OFF # Toggle the building of test suitesDebugging
By default, C++ logging is disabled. To enable logging, use:
export DEBUG=ONFor more detailed logging, adjust the glog verbosity level with:
export GLOG_v=10 # Set globally
GLOG_v=10 python3 ... # Set for a single commandTo further investigate issues like segmentation faults or other complexities, using gdb/lldb is recommended:
GLOG_v=10 gdb --args python3 -m pytest -sv tests/test_db_query.py
GLOG_v=10 lldb -- python3 -m pytest -sv tests/test_db_query.pyFor additional debugging techniques, refer to the documentation for gdb and lldb respectively.
Local Pre-Commit Checks
Before pushing code to GitHub, run local checks to catch issues early and save CI resources:
tips: A Python environment is required for the check. Set one up with
python3 -m venv .venv && source .venv/bin/activateorconda create -n neug python=3.13 && conda activate neug.
# Format check only (fast, recommended before commit)
make format-check
# Full check including build and tests (recommended before creating PR)
make full-checkThe format-check validates C++ (clang-format) and Python (isort, black, flake8) code formatting. Any auto-fixable issues (clang-format, isort, black) will be corrected in-place; only flake8 issues require manual fixes.
The full-check additionally compiles the code and runs unit tests.
For more options, see ./scripts/pre_commit_check.sh --help.
Building NeuG with NodeJS
We also provide NodeJS client. Both clients share a single root build tree at <repo>/build/.
If you have already built NeuG with Python, the library libneug.{dylib,so} can be shared.
NOTE: The NodeJS binding only supports AP mode. Exposing a raw HTTP port directly from a Node.js process to run TP mode is considered a dangerous practice — it bypasses the typical security layers (authentication, TLS termination, rate limiting, etc.) that a production server should have behind a proper reverse proxy or gateway. Therefore, the
serve()/SessionAPIs have been intentionally removed from the NodeJS binding.
If you really need TP mode, please deploy a dedicated NeuG server using the C++ or Python binding, and then connect to it from Node.js via standard HTTP clients.
For Development Purposes
From repo root:
make node-devOr from tools/nodejs_bind/:
cd tools/nodejs_bind && make devmake dev handles everything: installs npm deps (skipped if already installed),
configures cmake (cmake reuses cache internally if unchanged), builds, and stages
artifacts. Safe to run repeatedly.
Then NodeJS can automatically load these modules:
cd tools/nodejs_bind
const { Database } = require('./lib');Building NPM Package
The npm package is written to tools/nodejs_bind/neug-<version>-<platform>-<arch>.tgz,
which packs neug_node_bind.node and libneug.{dylib, so}.
make node-pack
# or:
cd tools/nodejs_bind && make packFAQ
ImportError: cannot import 'neug_py_bind'
The loader could not find neug_py_bind*.so. Check in this order:
- Did you actually build the target? Run
cd tools/python_bind && make dev— the.soshould appear at<repo>/build/tools/python_bind/. - Are you pointing at the right build tree? If you maintain multiple, set
NEUG_BUILD_DIR=/path/to/buildto override the default<repo>/build. - Inspect what the loader is looking for:
python3 -c "from neug import __init__; print(__init__._find_neug_py_bind_dir())"
Library not loaded: @rpath/libneug.dylib or transitive third-party libs
neug_py_bind*.so is set up with BUILD_RPATH=@loader_path/../../src so it
finds the sibling libneug.dylib automatically in the root build tree.
If you see this error:
- For
libneugitself: your build tree is incomplete or you moved files. Re-runcmake --build build --target neug_py_bind. - For a third-party dep (arrow, openssl, etc.):
libneug.dylib’s transitive deps weren’t found. SetDYLD_LIBRARY_PATH=/opt/neug/lib(macOS) orLD_LIBRARY_PATH=/opt/neug/lib:/opt/neug/lib64(Linux) to where they are installed, or rebuild with the deps statically linked.
To inspect what a .so is asking for:
otool -L build/tools/python_bind/neug_py_bind*.so # macOS
ldd build/tools/python_bind/neug_py_bind*.so # Linux