# Building libnbt++ ## Build System libnbt++ uses **CMake** (minimum version 3.15) as its build system. The root `CMakeLists.txt` defines the project, its options, source files, dependencies, and installation rules. --- ## Prerequisites ### Required - **C++11 compatible compiler**: GCC 4.8+, Clang 3.3+, or MSVC 2015+ - **CMake**: Version 3.15 or later ### Optional - **zlib**: Required for compressed NBT support (gzip/deflate). Enabled by default. - **CxxTest**: Required for building and running unit tests. Must be discoverable by CMake's `find_package(CxxTest)`. - **objcopy**: Required for test data embedding on Linux (binary test files are converted to object files via `objcopy`). --- ## CMake Options The following options are available when configuring the project: | Option | Default | Description | |-------------------|---------|----------------------------------------------------------| | `NBT_BUILD_SHARED` | `OFF` | Build as a shared (dynamic) library instead of static | | `NBT_USE_ZLIB` | `ON` | Enable zlib compression support | | `NBT_BUILD_TESTS` | `ON` | Build the unit test executables | | `NBT_NAME` | `nbt++` | Override the output library name | | `NBT_DEST_DIR` | (unset) | If set, enables install target with specified destination| ### Option Details #### NBT_BUILD_SHARED When `NBT_BUILD_SHARED=OFF` (default), a static library (`libnbt++.a` or `nbt++.lib`) is produced. When `NBT_BUILD_SHARED=ON`, a shared library is produced. In this case, CMake is configured to: - Set `CXX_VISIBILITY_PRESET` to `hidden` - Set `VISIBILITY_INLINES_HIDDEN` to `1` - Use the `NBT_EXPORT` macro (generated by `generate_export_header()`) to control symbol visibility This means only classes and functions explicitly marked with `NBT_EXPORT` are exported from the shared library. #### NBT_USE_ZLIB When enabled (default), the build: 1. Calls `find_package(ZLIB REQUIRED)` to locate the system zlib 2. Adds the zlib source files to the library: `src/io/izlibstream.cpp` and `src/io/ozlibstream.cpp` 3. Defines the preprocessor macro `NBT_HAVE_ZLIB` 4. Links the library against `ZLIB::ZLIB` The zlib headers (`include/io/izlibstream.h`, `include/io/ozlibstream.h`, `include/io/zlib_streambuf.h`) include `` directly. If zlib is not available, these headers cannot be included. #### NBT_NAME Allows overriding the library target name. By default, the target is called `nbt++`, producing `libnbt++.a`. Setting `NBT_NAME=mynbt` would produce `libmynbt.a`: ```cmake cmake -DNBT_NAME=mynbt .. ``` --- ## Source Files ### Core Library Sources The `NBT_SOURCES` variable lists all non-zlib source files: ```cmake set(NBT_SOURCES src/endian_str.cpp src/tag.cpp src/tag_compound.cpp src/tag_list.cpp src/tag_string.cpp src/value.cpp src/value_initializer.cpp src/io/stream_reader.cpp src/io/stream_writer.cpp src/text/json_formatter.cpp) ``` ### Zlib Sources (Conditional) Only added when `NBT_USE_ZLIB=ON`: ```cmake set(NBT_SOURCES_Z src/io/izlibstream.cpp src/io/ozlibstream.cpp) ``` --- ## Building Step by Step ### 1. Clone and Navigate ```bash git clone https://github.com/Project-Tick/Project-Tick.git cd Project-Tick/libnbtplusplus/ ``` ### 2. Create Build Directory ```bash mkdir build cd build ``` ### 3. Configure #### Default (static library, with zlib, with tests): ```bash cmake .. ``` #### Static library, no zlib, no tests: ```bash cmake -DNBT_USE_ZLIB=OFF -DNBT_BUILD_TESTS=OFF .. ``` #### Shared library: ```bash cmake -DNBT_BUILD_SHARED=ON .. ``` #### Custom library name: ```bash cmake -DNBT_NAME=nbtpp .. ``` #### Specify a different compiler: ```bash cmake -DCMAKE_CXX_COMPILER=clang++ .. ``` #### With install destination: ```bash cmake -DNBT_DEST_DIR=/usr/local/lib .. ``` ### 4. Build ```bash cmake --build . ``` Or with make directly: ```bash make -j$(nproc) ``` ### 5. Run Tests (if enabled) ```bash ctest --output-on-failure ``` ### 6. Install (optional) Only works if `NBT_DEST_DIR` was set: ```bash cmake --install . ``` --- ## Integration into Other Projects ### As a CMake Subdirectory The most common integration method is adding libnbt++ as a subdirectory in your project: ```cmake # In your project's CMakeLists.txt # Optional: disable tests for the dependency set(NBT_BUILD_TESTS OFF CACHE BOOL "" FORCE) add_subdirectory(libnbtplusplus) add_executable(myapp main.cpp) target_link_libraries(myapp nbt++) ``` The `target_include_directories` in libnbt++'s CMakeLists already uses `PUBLIC`, so include paths propagate automatically: ```cmake target_include_directories(${NBT_NAME} PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) ``` The `${CMAKE_CURRENT_BINARY_DIR}` is included because `generate_export_header()` creates `nbt_export.h` in the build directory. ### Include Paths After linking against the `nbt++` target, your code can include: ```cpp #include // All tag types #include // Reading #include // Writing #include // Decompression (if NBT_USE_ZLIB) #include // Compression (if NBT_USE_ZLIB) ``` ### Manually (without CMake) If not using CMake, you need to: 1. Add `libnbtplusplus/include/` to your include path 2. Compile all `.cpp` files in `src/` (and `src/io/`, `src/text/`) 3. If using zlib: add `-DNBT_HAVE_ZLIB`, link against `-lz` 4. Create your own `nbt_export.h` or define `NBT_EXPORT` as empty: ```cpp // nbt_export.h — manual version for static builds #ifndef NBT_EXPORT_H #define NBT_EXPORT_H #define NBT_EXPORT #endif ``` 5. Set C++ standard to C++11 or later: `-std=c++11` --- ## The nbt_export.h Header This header is **auto-generated** by CMake's `generate_export_header()` command at configure time. It is placed in `${CMAKE_CURRENT_BINARY_DIR}` and defines: - `NBT_EXPORT` — marks symbols for export from shared libraries - `NBT_NO_EXPORT` — marks symbols as hidden For static builds, `NBT_EXPORT` typically expands to nothing. For shared builds, it maps to compiler-specific visibility attributes: ```cpp // Example generated content (GCC/Clang) #define NBT_EXPORT __attribute__((visibility("default"))) #define NBT_NO_EXPORT __attribute__((visibility("hidden"))) ``` The binary directory is added to include paths so all source files can `#include "nbt_export.h"`. --- ## C++ Standard The library enforces C++11 via: ```cmake set_property(TARGET ${NBT_NAME} PROPERTY CXX_STANDARD 11) ``` This does not set `CXX_STANDARD_REQUIRED`, so CMake may use a higher standard if the compiler defaults to one. The code is compatible with C++11 through C++20+. --- ## Compile-Time Assertions The library includes several `static_assert` checks to ensure platform compatibility: In `src/tag.cpp`: ```cpp static_assert( std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, "The floating point values for NBT must conform to IEC 559/IEEE 754"); ``` In `src/endian_str.cpp`: ```cpp static_assert(CHAR_BIT == 8, "Assuming that a byte has 8 bits"); static_assert(sizeof(float) == 4, "Assuming that a float is 4 byte long"); static_assert(sizeof(double) == 8, "Assuming that a double is 8 byte long"); ``` These ensure that the platform's floating-point representation matches the NBT format's IEEE 754 requirement. --- ## Platform-Specific Notes ### Linux Tests are only supported on `x86_64` and `i686` architectures due to the use of `objcopy` for binary test data embedding: ```cmake if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL amd64) set(OBJCOPY_TARGET "elf64-x86-64") set(OBJCOPY_ARCH "x86_64") elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL i686) set(OBJCOPY_TARGET "elf32-i386") set(OBJCOPY_ARCH "i386") else() message(AUTHOR_WARNING "This is not a platform that would support testing nbt++") return() endif() ``` ### macOS / Windows The core library compiles on any platform with a C++11 compiler and optionally zlib. However, the test suite uses Linux-specific `objcopy` commands and may not build on non-Linux platforms without modifications. --- ## Shared Library Visibility When building as a shared library (`NBT_BUILD_SHARED=ON`), the CMake configuration applies strict visibility rules: ```cmake if(${BUILD_SHARED_LIBS}) set_target_properties(${NBT_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1) endif() ``` This means: - All symbols are hidden by default - Inline functions are also hidden - Only symbols marked `NBT_EXPORT` are exported This reduces binary size and prevents symbol collision when multiple libraries are loaded. --- ## Typical Build Output After a successful build with all options enabled, you will have: ``` build/ ├── libnbt++.a # The static library (or libnbt++.so for shared) ├── nbt_export.h # Generated export header └── test/ ├── nbttest # Core tag tests ├── endian_str_test # Endianness tests ├── read_test # Reading tests ├── write_test # Writing tests ├── zlibstream_test # Compression tests (if NBT_USE_ZLIB) ├── format_test # JSON formatter test └── test_value # Value assignment tests ``` --- ## Troubleshooting ### "Could not find ZLIB" Install the zlib development package: ```bash # Debian/Ubuntu sudo apt install zlib1g-dev # Fedora sudo dnf install zlib-devel # macOS brew install zlib ``` Or disable zlib: `cmake -DNBT_USE_ZLIB=OFF ..` ### "Could not find CxxTest" Install CxxTest: ```bash # Debian/Ubuntu sudo apt install cxxtest # macOS brew install cxxtest ``` Or disable tests: `cmake -DNBT_BUILD_TESTS=OFF ..` ### "nbt_export.h not found" This file is generated at configure time. Make sure you've run `cmake ..` (the configure step) before building. If building manually without CMake, create a minimal `nbt_export.h` as described in the manual integration section above. ### Linking Errors with Shared Builds If you see undefined symbol errors when linking against the shared library, ensure your code includes the correct headers and that `nbt_export.h` was generated during the shared build configuration. Verify `NBT_EXPORT` expands to the visibility attribute.