From ed9ec399c782ce299117de974319e00ee5dd5ee2 Mon Sep 17 00:00:00 2001 From: Markus Hennerbichler Date: Sat, 22 Apr 2023 16:15:35 +0100 Subject: [PATCH] Expose CTranslate2::ctranslate2 as CMake package (#1178) * Expose CTranslate2::ctranslate2 as CMake package * Add docs for C++ Library use * EOF new-line * Move COPY python upwards --- .gitignore | 4 ++ CMakeLists.txt | 72 ++++++++++++++++++++++++++---- cmake/ctranslate2Config.cmake | 1 + docker/Dockerfile | 3 +- docs/index.rst | 1 + docs/quickstart_cpp.md | 84 +++++++++++++++++++++++++++++++++++ 6 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 cmake/ctranslate2Config.cmake create mode 100644 docs/quickstart_cpp.md diff --git a/.gitignore b/.gitignore index fc5251280..9c6801c43 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ *.pyc /.vs /build + CMake*.json .idea python/build/ python/ctranslate2.egg-info/ python/dist/ +.cache +docs/build/ +docs/python/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 022843b30..4e91c3258 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,20 @@ if(APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13) endif() + +# Read version from version.py +file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/python/ctranslate2/version.py VERSION_FILE) +foreach(line IN LISTS VERSION_FILE) + if (line MATCHES "__version__") + string(REGEX MATCH "[0-9.]+" CTRANSLATE2_VERSION ${line}) + break() + endif() +endforeach() + +if(NOT CTRANSLATE2_VERSION) + message(FATAL_ERROR "Version can't be read from version.py") +endif() + if(MSVC) if(BUILD_SHARED_LIBS) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) @@ -72,9 +86,6 @@ endif() find_package(Threads) add_subdirectory(third_party/spdlog EXCLUDE_FROM_ALL) -set(PUBLIC_INCLUDE_DIRECTORIES - ${CMAKE_CURRENT_SOURCE_DIR}/include - ) set(PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/third_party @@ -499,12 +510,22 @@ else() add_library(${PROJECT_NAME} ${SOURCES}) endif() +include(GenerateExportHeader) +generate_export_header(${PROJECT_NAME}) +set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${CTRANSLATE2_VERSION}) +set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION 3) +set_property(TARGET ${PROJECT_NAME} PROPERTY + INTERFACE_${PROJECT_NAME}_MAJOR_VERSION 3) +set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY + COMPATIBLE_INTERFACE_STRING ${PROJECT_NAME}_MAJOR_VERSION +) + list(APPEND LIBRARIES ${CMAKE_DL_LIBS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARIES}) target_include_directories(${PROJECT_NAME} BEFORE - PUBLIC ${PUBLIC_INCLUDE_DIRECTORIES} + PUBLIC $ $ PRIVATE ${PRIVATE_INCLUDE_DIRECTORIES} - ) +) if(BUILD_TESTS) add_subdirectory(tests) @@ -517,13 +538,48 @@ if (BUILD_CLI) endif() install( - TARGETS ${PROJECT_NAME} + TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ) +) install( DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h*" - ) +) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION ${CTRANSLATE2_VERSION} + COMPATIBILITY AnyNewerVersion +) + +export(EXPORT ${PROJECT_NAME}Targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Targets.cmake" + NAMESPACE CTranslate2:: +) +configure_file(cmake/${PROJECT_NAME}Config.cmake + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake" + COPYONLY +) + +set(ConfigPackageLocation lib/cmake/${PROJECT_NAME}) +install(EXPORT ${PROJECT_NAME}Targets + FILE + ${PROJECT_NAME}Targets.cmake + NAMESPACE + CTranslate2:: + DESTINATION + ${ConfigPackageLocation} +) +install( + FILES + cmake/${PROJECT_NAME}Config.cmake + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION + ${ConfigPackageLocation} + COMPONENT + Devel +) diff --git a/cmake/ctranslate2Config.cmake b/cmake/ctranslate2Config.cmake new file mode 100644 index 000000000..8d131ecce --- /dev/null +++ b/cmake/ctranslate2Config.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/ctranslate2Targets.cmake") diff --git a/docker/Dockerfile b/docker/Dockerfile index d48770f33..d0e299fe7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -40,6 +40,8 @@ COPY third_party third_party COPY cli cli COPY include include COPY src src +COPY cmake cmake +COPY python python COPY CMakeLists.txt . ARG CXX_FLAGS @@ -60,7 +62,6 @@ RUN mkdir build && \ ENV LANG=en_US.UTF-8 COPY README.md . -COPY python python RUN cd python && \ python3 -m pip --no-cache-dir install -r install_requirements.txt && \ diff --git a/docs/index.rst b/docs/index.rst index 7533fd649..09bbc9a32 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,7 @@ The documentation includes installation instructions, usage guides, and API refe :maxdepth: 1 quickstart.md + quickstart_cpp.md installation.md .. toctree:: diff --git a/docs/quickstart_cpp.md b/docs/quickstart_cpp.md new file mode 100644 index 000000000..42a75f9bb --- /dev/null +++ b/docs/quickstart_cpp.md @@ -0,0 +1,84 @@ +# C++ Quickstart + +Start using the CTranslate2 library in your own C++ project. + +**1\. Compile and Install CTranslate2** + +```bash +mkdir build && cd build +cmake .. +make -j4 install +``` + +It's important that the library is getting installed into a directory that is on the CMAKE_PREFIX_PATH, otherwise you can install to a custom directory, e.g.: +```bash +export CTRANSLATE_INSTALL_PATH=$(pwd)/install +cmake .. -DCMAKE_INSTALL_PREFIX=$CTRANSLATE_INSTALL_PATH +make -j4 install +``` + + +See [installation guide][installation.md] for more info. + +**2\. Add CTranslate2 to your CMakeLists.txt** + +```cmake +cmake_minimum_required (VERSION 2.8.11) +project (CTRANSLATE2_DEMO) + +find_package(ctranslate2) + +add_executable (main main.cpp) +target_link_libraries(main CTranslate2::ctranslate2) +``` + + +**3\. Have a model ready in the CTranslate2 format** + +```bash +ct2-transformers-converter --model Helsinki-NLP/opus-mt-en-de --output_dir opus-mt-en-de +``` + +**4\. Write the translation C++ code using the API** + +You will need to have your input string tokenised, +which depends on what type of model you are using. +Have a look at the guides to get tokens from your input string. + +```cpp +#include +#include + +#include "ctranslate2/translator.h" + +int main(int argc, char* argv[]) { + + std::string model_path("opus-mt-en-de"); + const auto model = ctranslate2::models::Model::load(model_path); + const ctranslate2::models::ModelLoader model_loader(model_path); + ctranslate2::Translator translator(model_loader); + + std::vector> batch = {{"▁Hello", "▁World", "!", ""}}; + auto translation = translator.translate_batch(batch); + for (auto &token: translation[0].output()) { + std::cout << token << ' '; + } + std::cout << std::endl; +} +``` +**5\. Compile and run the example** +```bash +cmake . +make +./main +``` +If you have installed the CTranslate lib to a custom path use: `cmake -DCMAKE_PREFIX_PATH=$CTRANSLATE_INSTALL_PATH .` + + +This code should print the output tokens: + +> ▁Hall o ▁Welt + +If that's the case, you successfully converted and executed a translation model with CTranslate2! +(De)tokenisation is handled outside of CTranslate2, +so make sure to properly tokenise the input and detokenise the output. \ No newline at end of file