# 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 `<zlib.h>` 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 <nbt_tags.h>           // All tag types
#include <io/stream_reader.h>   // Reading
#include <io/stream_writer.h>   // Writing
#include <io/izlibstream.h>     // Decompression (if NBT_USE_ZLIB)
#include <io/ozlibstream.h>     // 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<float>::is_iec559 &&
    std::numeric_limits<double>::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.
