diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2e889c4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +/build* +/.vscode +/cpm_modules +.DS_Store +/.github +/standalone/Dockerfile diff --git a/.gitignore b/.gitignore index d54a4f4..8ad95d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /build* /.vscode /cpm_modules -.DS_Store \ No newline at end of file +.DS_Store diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 3c875b8..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -FROM ubuntu:20.04 as build -# install dev tools -ENV DEBIAN_FRONTEND=noninteractive -RUN set -eux; \ - apt-get update; \ - apt-get install -y --no-install-recommends \ - ca-certificates \ - make \ - cmake \ - git \ - gcc \ - g++ \ - ; \ - rm -rf /var/lib/apt/lists/* -# build in release mode -WORKDIR /build-all -COPY ./all ./all -COPY ./cmake ./cmake -COPY ./documentation ./documentation -COPY ./include ./include -COPY ./source ./source -COPY ./standalone ./standalone -COPY ./test ./test -COPY ./CMakeLists.txt . -RUN cmake -S all -B build -D CMAKE_BUILD_TYPE=Release -RUN cmake --build build - -# make a test image - could also be done directly from/in the build image -FROM ubuntu:20.04 as test -WORKDIR /test -COPY --from=build /build-all/build/test/GreeterTests ./ -CMD ["./GreeterTests"] - -# make an image to deploy our 'app' -FROM ubuntu:20.04 as deploy -WORKDIR /app -COPY --from=build /build-all/build/standalone/Greeter ./ -CMD ["./Greeter"] diff --git a/standalone/CMakeLists.txt b/standalone/CMakeLists.txt index a932149..efdbb08 100644 --- a/standalone/CMakeLists.txt +++ b/standalone/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.14...3.22) -project(GreeterStandalone LANGUAGES CXX) +project( + GreeterStandalone + DESCRIPTION "A standalone minimal webserver application using the Crow framework" + LANGUAGES CXX +) # --- Import tools ---- @@ -10,12 +14,44 @@ include(../cmake/tools.cmake) include(../cmake/CPM.cmake) +# Crow needs Boost 1.64 and does not provide CPM.cmake integration itself, so we have to get Boost first +find_package(Boost 1.64 QUIET) +if(NOT Boost_FOUND) + # Use CPM.cmake to get Boost from the official repo if not provided as system lib + message(STATUS "GreeterStandalone: Boost system lib NOT found") + CPMAddPackage( + NAME Boost + GITHUB_REPOSITORY boostorg/boost + GIT_TAG boost-1.78.0 + VERSION 1.78.0 + ) + # Ugly workaround: + # Boost cmake support is still experimental, the Boost::boost target is not provided if downloaded via + # FetchContent_declare / CPM.cmake + # Crow uses it for asio, so we fake the Boost:boost target as asio + if(NOT TARGET Boost::boost) + add_library(Boost::boost INTERFACE IMPORTED) + target_link_libraries(Boost::boost INTERFACE Boost::asio) + endif() +else() + message(STATUS "GreeterStandalone: Boost system lib found") +endif() +# add Crow CPMAddPackage( +<<<<<<< HEAD GITHUB_REPOSITORY jarro2783/cxxopts VERSION 3.0.0 OPTIONS "CXXOPTS_BUILD_EXAMPLES NO" "CXXOPTS_BUILD_TESTS NO" "CXXOPTS_ENABLE_INSTALL YES" +======= + NAME Crow + GITHUB_REPOSITORY CrowCpp/Crow + GIT_TAG v1.0+1 + VERSION 1.0.1 + OPTIONS "CROW_INSTALL OFF" +>>>>>>> a42d7c5 (add boost, make boost work in docker) ) +# get the Greeter lib CPMAddPackage(NAME Greeter SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) # ---- Create standalone executable ---- @@ -23,7 +59,5 @@ CPMAddPackage(NAME Greeter SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) file(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp) add_executable(${PROJECT_NAME} ${sources}) - -set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 OUTPUT_NAME "Greeter") - -target_link_libraries(${PROJECT_NAME} Greeter::Greeter cxxopts) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11) +target_link_libraries(${PROJECT_NAME} PRIVATE Greeter::Greeter Crow::Crow) diff --git a/standalone/Dockerfile b/standalone/Dockerfile new file mode 100644 index 0000000..30989e4 --- /dev/null +++ b/standalone/Dockerfile @@ -0,0 +1,21 @@ +# build +FROM buildpack-deps:bullseye as webapp-build +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + cmake \ + ninja-build \ + # build with Boost as system lib - this should be orders of magnitude faster to configure than + # downloading via CPM.cmake while Boost's CMake support is still experimental + libboost-all-dev \ + ; +COPY . . +RUN cmake -S standalone -B build -G Ninja -D CMAKE_BUILD_TYPE=Release +RUN cmake --build build + + +# deploy +FROM debian:bullseye-slim as webapp-run +WORKDIR /app +COPY --from=webapp-build /build/GreeterStandalone . +CMD ["./GreeterStandalone"] diff --git a/standalone/source/main.cpp b/standalone/source/main.cpp index 938a386..6c3f728 100644 --- a/standalone/source/main.cpp +++ b/standalone/source/main.cpp @@ -1,53 +1,48 @@ +#include #include #include -#include #include #include #include -auto main(int argc, char** argv) -> int { - const std::unordered_map languages{ - {"en", greeter::LanguageCode::EN}, - {"de", greeter::LanguageCode::DE}, - {"es", greeter::LanguageCode::ES}, - {"fr", greeter::LanguageCode::FR}, - }; +int main() { + crow::SimpleApp app; - cxxopts::Options options(*argv, "A program to welcome the world!"); + CROW_ROUTE(app, "/hello") + ([](const crow::request& req) { + // check params + std::cout << "Params: " << req.url_params << "\n"; + std::cout << "The key 'language' was " + << (req.url_params.get("language") == nullptr ? "not " : "") << "found.\n"; - std::string language; - std::string name; + if (req.url_params.get("language") == nullptr) { + // return bad request + return crow::response(400, "please provide a 'language' argument"); + } + const auto language = req.url_params.get("language"); - // clang-format off - options.add_options() - ("h,help", "Show help") - ("v,version", "Print the current version number") - ("n,name", "Name to greet", cxxopts::value(name)->default_value("World")) - ("l,lang", "Language code to use", cxxopts::value(language)->default_value("en")) - ; - // clang-format on + // see if langauge was found + const std::unordered_map languages{ + {"en", greeter::LanguageCode::EN}, + {"de", greeter::LanguageCode::DE}, + {"es", greeter::LanguageCode::ES}, + {"fr", greeter::LanguageCode::FR}, + }; + const auto langIt = languages.find(language); + if (langIt == languages.end()) { + // return bad request + std::cout << "Greeting for language '" << language << "' is not available\n"; + return crow::response(400, "language not recognized"); + } - auto result = options.parse(argc, argv); + const greeter::Greeter greeter("Crow & Greeter"); + std::cout << "Greeting for language '" << language << "' is available, returning message\n"; + const auto message = greeter.greet(langIt->second); - if (result["help"].as()) { - std::cout << options.help() << std::endl; - return 0; - } + crow::json::wvalue ret({{"answer", message}}); + return crow::response(200, ret); + }); - if (result["version"].as()) { - std::cout << "Greeter, version " << GREETER_VERSION << std::endl; - return 0; - } - - auto langIt = languages.find(language); - if (langIt == languages.end()) { - std::cerr << "unknown language code: " << language << std::endl; - return 1; - } - - greeter::Greeter greeter(name); - std::cout << greeter.greet(langIt->second) << std::endl; - - return 0; + app.port(3080).multithreaded().run(); }