diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index bfd74d8..d0cf18c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,27 +2,14 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04 AS base RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ && apt-get -y install --no-install-recommends \ - build-essential clang lld make gdb cmake ninja-build \ - git curl wget unzip sudo \ - pip \ - clang-format clang-tidy \ - libpq-dev \ + build-essential clang lld make gdb cmake ninja-build \ + git curl wget unzip sudo \ + pip \ + clang-format clang-tidy \ && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* RUN pip install conan -ARG ODB_VERSION=2.5.0 - -RUN mkdir -p /opt/odb && \ - wget -q -nc -P /opt/odb \ - https://www.codesynthesis.com/download/odb/${ODB_VERSION}/ubuntu/ubuntu22.04/x86_64/libodb_${ODB_VERSION}-0~ubuntu22.04_amd64.deb \ - https://www.codesynthesis.com/download/odb/${ODB_VERSION}/ubuntu/ubuntu22.04/x86_64/libodb-dev_${ODB_VERSION}-0~ubuntu22.04_amd64.deb \ - https://www.codesynthesis.com/download/odb/${ODB_VERSION}/ubuntu/ubuntu22.04/x86_64/libodb-pgsql_${ODB_VERSION}-0~ubuntu22.04_amd64.deb \ - https://www.codesynthesis.com/download/odb/${ODB_VERSION}/ubuntu/ubuntu22.04/x86_64/libodb-pgsql-dev_${ODB_VERSION}-0~ubuntu22.04_amd64.deb \ - https://www.codesynthesis.com/download/odb/${ODB_VERSION}/ubuntu/ubuntu22.04/x86_64/odb_${ODB_VERSION}-0~ubuntu22.04_amd64.deb - -RUN dpkg -i /opt/odb/*.deb && rm -rf /opt/odb - COPY to-dos-conan-profile.conf /root/.conan2/profiles/default \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fd58d96..aaf2351 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,7 +10,7 @@ // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "conan profile detect --force", + "postCreateCommand": "conan remote add local-recipes ./deps --type=local-recipes-index", // Add a local recipes store for odb libraries // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. diff --git a/.devcontainer/to-dos-conan-profile.conf b/.devcontainer/to-dos-conan-profile.conf index 71729b1..2c7b3fd 100644 --- a/.devcontainer/to-dos-conan-profile.conf +++ b/.devcontainer/to-dos-conan-profile.conf @@ -9,4 +9,5 @@ compiler.cppstd=gnu20 [conf] tools.build:compiler_executables={"c":"clang","cpp":"clang++"} +tools.build:jobs={{os.cpu_count()}} tools.system.package_manager:mode=install \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a4f159d..571c090 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,6 +33,7 @@ jobs: cp .devcontainer/to-dos-conan-profile.conf .conan2/profiles/default export CONAN_HOME="$(pwd)/.conan2" + conan remote add local-recipes ./deps --type=local-recipes-index conan install . --build=missing # we don't need to push docker image that was built using our Dev Container push: never diff --git a/CMakeLists.txt b/CMakeLists.txt index f61829c..6f56a1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,19 +11,34 @@ set(PROJECT_OBJECTS ${PROJECT_NAME}_lib) add_library(${PROJECT_OBJECTS} OBJECT ${sources}) +file(GLOB_RECURSE database_models + ${CMAKE_CURRENT_SOURCE_DIR}/src/data/models/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/data/models/*.hxx +) + +set(database_model_dirs "") +foreach(header ${database_models}) + get_filename_component(dir ${header} DIRECTORY) + list(APPEND database_model_dirs ${dir}) +endforeach() +list(REMOVE_DUPLICATES database_model_dirs) + target_include_directories(${PROJECT_OBJECTS} PUBLIC ${CMAKE_SOURCE_DIR}/src /usr/include - src/data/models/ + ${database_model_dirs} ) find_package(Drogon REQUIRED) find_package(jsoncpp REQUIRED) +find_package(libodb REQUIRED) +find_package(libodb-pgsql REQUIRED) target_link_libraries(${PROJECT_OBJECTS} PUBLIC Drogon::Drogon JsonCpp::JsonCpp - odb odb-pgsql + libodb::libodb + libodb-pgsql::libodb-pgsql ) add_executable(${PROJECT_NAME} diff --git a/README.md b/README.md index 09b1886..c529a7a 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,10 @@ Alternatively, use the CMake Tools Extension in VS Code. To do this, open the CM ### ORM: -After creating the database or model, it is necessary to generate the auxiliary ODB files with the command `odb --std c++20 -d pgsql --generate-query -o odb-gen ` from the folder `src/data/models`. +After creating the database or model, it is necessary to generate the auxiliary ODB files. +For generating these files use script `./scripts/generate_odb_files.sh` -After executing the command, files will be created or updated in the folder `src/data/models/odb-gen'. Please do not modify or transfer these files for the correct operation of the application. +After executing the script, files will be created or updated in the folder `src/data/models/odb-gen'. Please do not modify or transfer these files for the correct operation of the application. ### Migrations: diff --git a/conanfile.py b/conanfile.py index 0307e98..094274a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -43,5 +43,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("drogon/1.9.10") + self.requires("drogon/1.9.12") self.requires("gtest/1.14.0") + self.requires("libodb/2.5.0") + self.requires("libodb-pgsql/2.5.0") diff --git a/deps/recipes/libodb-pgsql/all/conanfile.py b/deps/recipes/libodb-pgsql/all/conanfile.py new file mode 100644 index 0000000..ae3aca8 --- /dev/null +++ b/deps/recipes/libodb-pgsql/all/conanfile.py @@ -0,0 +1,154 @@ +import os +import textwrap +from conan import ConanFile +from conan.errors import ConanInvalidConfiguration +from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout +from conan.tools.files import get, save, rmdir + +class LibOdbPgsqlConan(ConanFile): + name = "libodb-pgsql" + version = "2.5.0" + license = "GPL-2.0-only" + homepage = "https://www.codesynthesis.com/products/odb/" + topics = ("odb", "orm", "database", "c++") + + settings = "os", "compiler", "build_type", "arch" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + + def config_options(self): + pass + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self, src_folder="src") + + def validate(self): + if self.settings.os != "Linux": + raise ConanInvalidConfiguration( + f"{self.ref} is supported only on Linux" + ) + try: + with open("/etc/os-release") as f: + info = dict( + line.strip().split("=", 1) + for line in f if "=" in line + ) + distro = info.get("ID", "").strip('"') + version = info.get("VERSION_ID", "").strip('"') + if distro != "ubuntu" or version != "22.04": + raise ConanInvalidConfiguration( + f"{self.ref} is supported only on Ubuntu 22.04 " + f"(detected: {distro} {version})" + ) + except FileNotFoundError: + raise ConanInvalidConfiguration( + f"{self.ref} requires Ubuntu 22.04 (/etc/os-release not found)" + ) + + def requirements(self): + self.requires("libodb/2.5.0") + self.requires("libpq/17.7") + + def source(self): + get( + self, + url=f"https://www.codesynthesis.com/download/odb/{self.version}/libodb-pgsql-{self.version}.tar.gz", + sha256="f6e63db4a2f77604f48115f64c74a5854ca20f03f208568966693e95712a3e17", # replace hash if you changing sources + strip_root=True, + ) + self._inject_cmake() + + def _inject_cmake(self): + major = self.version.split(".")[0] + cmake = textwrap.dedent(f"""\ + cmake_minimum_required(VERSION 3.15) + project(odb-pgsql VERSION {self.version} LANGUAGES CXX) + + find_package(libodb REQUIRED CONFIG) + find_package(PostgreSQL REQUIRED CONFIG) + + file(GLOB ODB_SOURCES "odb/pgsql/*.cxx") + + file(GLOB ODB_PREGENERATED_SOURCES + "odb/pgsql/details/pregenerated/odb/pgsql/details/*.cxx" + ) + + add_library(odb-pgsql + ${{ODB_SOURCES}} + ${{ODB_PREGENERATED_SOURCES}} + ) + + target_include_directories(odb-pgsql PUBLIC + $ + $ + $ + ) + + target_compile_features(odb-pgsql PUBLIC cxx_std_11) + + target_link_libraries(odb-pgsql PUBLIC + libodb::libodb + PostgreSQL::PostgreSQL + ) + + set_target_properties(odb-pgsql PROPERTIES + VERSION {self.version} + SOVERSION {major} + POSITION_INDEPENDENT_CODE ON + ) + + install(TARGETS odb-pgsql + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + ) + + install(DIRECTORY odb/pgsql + DESTINATION include/odb + FILES_MATCHING + PATTERN "*.hxx" + PATTERN "*.ixx" + PATTERN "*.txx" + PATTERN "*.h" + PATTERN "pregenerated" EXCLUDE + ) + + install(DIRECTORY odb/pgsql/details/pregenerated/odb/pgsql/details + DESTINATION include/odb/pgsql + FILES_MATCHING + PATTERN "*.hxx" + PATTERN "*.ixx" + ) + """) + save(self, os.path.join(self.source_folder, "CMakeLists.txt"), cmake) + + def generate(self): + tc = CMakeToolchain(self) + tc.generate() + + deps = CMakeDeps(self) + deps.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig")) + rmdir(self, os.path.join(self.package_folder, "share")) + + def package_info(self): + self.cpp_info.libs = ["odb-pgsql"] + self.cpp_info.requires = [ + "libodb::libodb", + "libpq::pq", + ] + self.cpp_info.set_property("cmake_target_name", "libodb-pgsql::libodb-pgsql") + self.cpp_info.set_property("pkg_config_name", "libodb-pgsql") diff --git a/deps/recipes/libodb-pgsql/config.yml b/deps/recipes/libodb-pgsql/config.yml new file mode 100644 index 0000000..fca5391 --- /dev/null +++ b/deps/recipes/libodb-pgsql/config.yml @@ -0,0 +1,3 @@ +versions: + "2.5.0": + folder: all \ No newline at end of file diff --git a/deps/recipes/libodb/all/conanfile.py b/deps/recipes/libodb/all/conanfile.py new file mode 100644 index 0000000..317ec35 --- /dev/null +++ b/deps/recipes/libodb/all/conanfile.py @@ -0,0 +1,124 @@ +import os +import textwrap +from conan import ConanFile +from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout +from conan.tools.files import get, save, rmdir + + +class LibOdbConan(ConanFile): + name = "libodb" + version = "2.5.0" + description = "ODB C++ ORM — core runtime library" + license = "GPL-2.0-only" + homepage = "https://www.codesynthesis.com/products/odb/" + topics = ("odb", "orm", "database", "c++") + + settings = "os", "compiler", "build_type", "arch" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self, src_folder="src") + + def validate(self): + if self.settings.os != "Linux": + raise ConanInvalidConfiguration( + f"{self.ref} is supported only on Linux" + ) + try: + with open("/etc/os-release") as f: + info = dict( + line.strip().split("=", 1) + for line in f if "=" in line + ) + distro = info.get("ID", "").strip('"') + version = info.get("VERSION_ID", "").strip('"') + if distro != "ubuntu" or version != "22.04": + raise ConanInvalidConfiguration( + f"{self.ref} is supported only on Ubuntu 22.04 " + f"(detected: {distro} {version})" + ) + except FileNotFoundError: + raise ConanInvalidConfiguration( + f"{self.ref} requires Ubuntu 22.04 (/etc/os-release not found)" + ) + + def source(self): + get( + self, + url=f"https://www.codesynthesis.com/download/odb/{self.version}/libodb-{self.version}.tar.gz", + sha256="700038a73c6cbead011129b15030b7cdd3f73510b687f2c4504808df4230441b", # replace hash if you changing sources + strip_root=True, + ) + self._inject_cmake() + + def _inject_cmake(self): + major = self.version.split(".")[0] + cmake = textwrap.dedent(f"""\ + cmake_minimum_required(VERSION 3.15) + project(libodb VERSION {self.version} LANGUAGES CXX) + + file(GLOB ODB_SOURCES "odb/*.cxx") + file(GLOB_RECURSE ODB_DETAILS_SOURCES "odb/details/*.cxx") + + add_library(odb + ${{ODB_SOURCES}} + ${{ODB_DETAILS_SOURCES}} + ) + + target_include_directories(odb PUBLIC + $ + $ + ) + + target_compile_features(odb PUBLIC cxx_std_11) + + find_package(Threads REQUIRED) + target_compile_definitions(odb PUBLIC ODB_THREADS_POSIX) + target_link_libraries(odb PUBLIC Threads::Threads) + + set_target_properties(odb PROPERTIES + VERSION {self.version} + SOVERSION {major} + ) + + install(TARGETS odb + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + ) + install(DIRECTORY odb/ + DESTINATION include/odb + FILES_MATCHING + PATTERN "*.hxx" + PATTERN "*.ixx" + PATTERN "*.txx" + PATTERN "*.h" + ) + """) + save(self, os.path.join(self.source_folder, "CMakeLists.txt"), cmake) + + def generate(self): + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig")) + rmdir(self, os.path.join(self.package_folder, "share")) + + def package_info(self): + self.cpp_info.libs = ["odb"] + self.cpp_info.system_libs = ["pthread"] + self.cpp_info.set_property("cmake_target_name", "libodb::libodb") + self.cpp_info.set_property("pkg_config_name", "libodb") diff --git a/deps/recipes/libodb/config.yml b/deps/recipes/libodb/config.yml new file mode 100644 index 0000000..fca5391 --- /dev/null +++ b/deps/recipes/libodb/config.yml @@ -0,0 +1,3 @@ +versions: + "2.5.0": + folder: all \ No newline at end of file diff --git a/scripts/generate_odb_files.sh b/scripts/generate_odb_files.sh new file mode 100644 index 0000000..0ea162e --- /dev/null +++ b/scripts/generate_odb_files.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail + +ODB_DEB_URL="https://www.codesynthesis.com/download/odb/2.5.0/ubuntu/ubuntu22.04/x86_64/odb_2.5.0-0~ubuntu22.04_amd64.deb" +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +MODELS_DIR="$ROOT_DIR/src/data/models" +ODB_OUT_DIR="$MODELS_DIR/odb-gen" + +# ── Resolve Conan include paths ─────────────────────────────── +echo "[0/3] Resolving Conan include paths..." +ODB_INCLUDE="$(conan cache path libodb/2.5.0)/../s/src" +PGSQL_INCLUDE="$(conan cache path libodb-pgsql/2.5.0)/../s/src" +echo " libodb → $ODB_INCLUDE" +echo " libodb-pg → $PGSQL_INCLUDE" + +# ── Install ─────────────────────────────────────────────────── +echo "[1/3] Installing ODB..." +tmp=$(mktemp -d) +curl -fsSL -o "$tmp/odb.deb" "$ODB_DEB_URL" +sudo apt-get install -y "$tmp/odb.deb" +rm -rf "$tmp" + +# ── Generate ────────────────────────────────────────────────── +echo "[2/3] Generating ODB files..." +mkdir -p "$ODB_OUT_DIR" + +find "$MODELS_DIR" -type f \( -name "*.hxx" -o -name "*.h" \) \ + -not -path "$ODB_OUT_DIR/*" -print0 | \ +while IFS= read -r -d '' header; do + echo " → $(basename "$header")" + odb --std c++20 -d pgsql --generate-query \ + -o "$ODB_OUT_DIR" \ + -I "$ODB_INCLUDE" \ + -I "$PGSQL_INCLUDE" \ + -I "$ROOT_DIR" \ + "$header" +done + +# ── Cleanup ─────────────────────────────────────────────────── +echo "[3/3] Removing ODB..." +sudo apt-get remove -y odb + +echo "Done. Output: $ODB_OUT_DIR"