diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 603ead0..175e6de 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -1,34 +1,30 @@ -name: libcosimc CI Conan +name: CI # This workflow is triggered on pushes to the repository. on: [push, workflow_dispatch] jobs: - conan-on-linux: - name: Conan + linux: + name: Linux runs-on: ubuntu-latest strategy: fail-fast: false matrix: build_type: [Debug, Release] compiler_version: [9] - compiler_libcxx: [libstdc++11] + option_shared: ['shared=True', 'shared=False'] option_proxyfmu: ['proxyfmu=True', 'proxyfmu=False'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Generate Dockerfile run: | mkdir /tmp/osp-builder-docker cat <<'EOF' >/tmp/osp-builder-docker/Dockerfile - FROM conanio/gcc${{ matrix.compiler_version }} - USER root - RUN apt-get update && apt-get install -y --force-yes doxygen + FROM conanio/gcc${{ matrix.compiler_version }}-ubuntu16.04 ENV CONAN_LOGIN_USERNAME_OSP=${{ secrets.osp_artifactory_usr }} ENV CONAN_PASSWORD_OSP=${{ secrets.osp_artifactory_pwd }} - ENV CONAN_REVISIONS_ENABLED=1 - ENV CONAN_NON_INTERACTIVE=1 - ENV CONAN_USE_ALWAYS_SHORT_PATHS=1 + ENV LIBCOSIMC_RUN_TESTS_ON_CONAN_BUILD=1 COPY entrypoint.sh / ENTRYPOINT /entrypoint.sh EOF @@ -47,42 +43,47 @@ jobs: SHORT_REFNAME="${REFNAME:0:40}" CHANNEL="testing-${SHORT_REFNAME//\//_}" fi - conan create -s build_type=${{ matrix.build_type }} -s compiler.version=${{ matrix.compiler_version }} -s compiler.libcxx=${{ matrix.compiler_libcxx }} -o libcosim:${{ matrix.option_proxyfmu }} --build missing . osp/${CHANNEL} - conan upload --all -c -r osp 'libcosimc*' + conan create \ + --settings="build_type=${{ matrix.build_type }}" \ + --options="libcosimc/*:${{ matrix.option_shared }}" \ + --options="libcosim/*:${{ matrix.option_proxyfmu }}" \ + --build=missing \ + --user=osp \ + --channel="${CHANNEL}" \ + . + conan upload --confirm --remote=osp 'libcosimc/*' EOF chmod 0755 /tmp/osp-builder-docker/entrypoint.sh - name: Build Docker image - run: | - docker build -t osp-builder /tmp/osp-builder-docker/ + run: docker build -t osp-builder /tmp/osp-builder-docker/ - name: Build libcosimc - run: | - docker run --rm --env GITHUB_REF="$GITHUB_REF" -v $(pwd):/mnt/source:ro osp-builder + run: docker run --rm --env GITHUB_REF="$GITHUB_REF" -v $(pwd):/mnt/source:ro osp-builder - conan-on-windows: - name: Conan + windows: + name: Windows runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [windows-2019] build_type: [Debug, Release] + option_shared: ['shared=True', 'shared=False'] option_proxyfmu: ['proxyfmu=True', 'proxyfmu=False'] env: CONAN_LOGIN_USERNAME_OSP: ${{ secrets.osp_artifactory_usr }} CONAN_PASSWORD_OSP: ${{ secrets.osp_artifactory_pwd }} - CONAN_REVISIONS_ENABLED: 1 - CONAN_NON_INTERACTIVE: 1 - CONAN_USE_ALWAYS_SHORT_PATHS: 1 + LIBCOSIMC_RUN_TESTS_ON_CONAN_BUILD: 1 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install prerequisites run: | pip3 install --upgrade setuptools pip - pip3 install conan==1.59 - choco install doxygen.install + pip3 install conan - name: Configure Conan - run: conan remote add osp https://osp.jfrog.io/artifactory/api/conan/conan-local --force + run: | + conan profile detect + conan remote add osp https://osp.jfrog.io/artifactory/api/conan/conan-local --force - name: Conan create shell: bash run: | @@ -94,6 +95,13 @@ jobs: SHORT_REFNAME="${REFNAME:0:40}" CHANNEL="testing-${SHORT_REFNAME//\//_}" fi - conan create -s build_type=${{ matrix.build_type }} -o libcosim:${{ matrix.option_proxyfmu }} . osp/${CHANNEL} -b missing + conan create \ + --settings="build_type=${{ matrix.build_type }}" \ + --options="libcosimc*:${{ matrix.option_shared }}" \ + --options="libcosim/*:${{ matrix.option_proxyfmu }}" \ + --build=missing \ + --user=osp \ + --channel="${CHANNEL}" \ + . - name: Conan upload - run: conan upload --all -c -r osp 'libcosimc*' + run: conan upload --confirm --remote=osp 'libcosimc/*' diff --git a/.gitignore b/.gitignore index 5b3453a..ce39770 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,8 @@ .idea cmake-build-*/ - +build/ +CMakeUserPresets.json ### Vim -*.swp \ No newline at end of file +*.swp diff --git a/CMakeLists.txt b/CMakeLists.txt index b06268a..cbb6fe5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.15) file(STRINGS "${CMAKE_SOURCE_DIR}/version.txt" projectVersion) project("libcosimc" VERSION "${projectVersion}" @@ -16,7 +16,6 @@ option(BUILD_SHARED_LIBS "Build shared libraries instead of static libraries" ON option(LIBCOSIMC_TREAT_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" ON) option(LIBCOSIMC_BUILD_TESTS "Build test suite" ON) option(LIBCOSIMC_STANDALONE_INSTALLATION "Whether to build for a standalone installation (Linux only; sets a relative RPATH)" OFF) -option(LIBCOSIMC_USING_CONAN "Whether Conan is used for package management" OFF) # ============================================================================== @@ -62,21 +61,10 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") string(REPLACE "/W3" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) string(REPLACE "/W3" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) endif() - add_compile_options("/wd4996") + add_compile_options("/wd4251" "/wd4996") if(LIBCOSIMC_TREAT_WARNINGS_AS_ERRORS) add_compile_options("/WX") endif() - if(LIBCOSIMC_USING_CONAN) - # C4251: 'identifier' : class 'type' needs to have dll-interface to be - # used by clients of class 'type2' - # - # This could be a problem if a dependent DLL is compiled against a - # different version or variant of the C++ runtime than the one we - # use. An example would be if Boost was built in release mode and - # we are compiling in release mode. However, when we use Conan, this - # should not be a problem, so we disable the warning. - add_compile_options("/wd4251") - endif() add_definitions("-D_SCL_SECURE_NO_WARNINGS" "-D_CRT_SECURE_NO_WARNINGS") endif() @@ -106,17 +94,7 @@ set(LIBCOSIMC_EXPORT_TARGET "${PROJECT_NAME}-targets") # Dependencies # ============================================================================== -if(LIBCOSIMC_USING_CONAN) - if(EXISTS ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) - include("${CMAKE_BINARY_DIR}/conanbuildinfo.cmake") - conan_basic_setup(NO_OUTPUT_DIRS) - else() - message(FATAL_ERROR "The file conanbuildinfo.cmake doesn't exist, you have to run conan install first") - endif() -endif() - find_package(libcosim REQUIRED) -find_package(Boost REQUIRED COMPONENTS fiber) # ============================================================================== # Targets @@ -140,7 +118,7 @@ add_library(cosimc "include/cosim.h" "src/cosim.cpp" ${generatedSourcesFull}) target_compile_features(cosimc PRIVATE "cxx_std_17") target_include_directories(cosimc PUBLIC "$") -target_link_libraries(cosimc PUBLIC libcosim::cosim Boost::fiber) +target_link_libraries(cosimc PUBLIC libcosim::cosim) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_link_libraries(cosimc PUBLIC stdc++) endif() diff --git a/README.md b/README.md index 5ecc176..d350a30 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,23 @@ libcosimc - OSP C co-simulation API =================================== ![libcosimc CI Conan](https://github.com/open-simulation-platform/libcosimc/workflows/libcosimc%20CI%20Conan/badge.svg) - -This repository contains the OSP C library for co-simulations which wraps and exposes a subset of the [`libcosim`] -library's functions. - + +This repository contains the [OSP] C library for co-simulations, which wraps and +exposes a subset of the [libcosim] C++ library's functions. + See [`CONTRIBUTING.md`] for contributor guidelines and [`LICENSE`] for terms of use. - - + How to build ------------ +Please read the [libcosim build instructions]. The commands you should run +to build libcosimc are exactly the same, except that the option you need to +add to `conan install` to enable [proxy-fmu] support is + `--options="libcosim/*:proxyfmu=True`. -`libcosimc` can be built in the same way as libcosim with the following differences in [step 2] - -To include FMU-proxy support use `-o libcosim:'proxyfmu=True'` when installing dependencies in - - conan install .. -o libcosim:'proxyfmu=True' --build=missing - -When running cmake use `-DLIBCOSIMC_USING_CONAN=TRUE` - [`CONTRIBUTING.md`]: ./CONTRIBUTING.md +[libcosim]: https://github.com/open-simulation-platform/libcosim +[libcosim build instructions]: https://github.com/open-simulation-platform/libcosim#how-to-build [`LICENSE`]: ./LICENSE -[Step 2]: https://github.com/open-simulation-platform/libcosim#step-2-prepare-build-system -[`libcosim`]: https://github.com/open-simulation-platform/libcosim +[OSP]: https://opensimulationplatform.com/ +[proxy-fmu]: https://github.com/open-simulation-platform/proxy-fmu/ diff --git a/conanfile.py b/conanfile.py index f5af25f..70a82a2 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,47 +1,87 @@ import os -from conans import ConanFile, CMake, tools -from os import path +from conan import ConanFile +from conan.tools.cmake import CMake, cmake_layout +from conan.tools.env import VirtualRunEnv +from conan.tools.files import load class LibCosimCConan(ConanFile): + # Basic package info name = "libcosimc" + + def set_version(self): + self.version = load(self, os.path.join(self.recipe_folder, "version.txt")).strip() + + # Metadata + license = "MPL-2.0" author = "osp" - exports = "version.txt" - scm = { - "type": "git", - "url": "auto", - "revision": "auto" - } + description = "A C wrapper for libcosim, a co-simulation library for C++" + + # Binary configuration + package_type = "library" settings = "os", "compiler", "build_type", "arch" - generators = "cmake", "virtualrunenv" + options = { + "shared": [True, False], + "fPIC": [True, False], + } + default_options = { + "shared": True, + "fPIC": True, + } + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + self.options["*"].shared = self.options.shared + + # Dependencies/requirements + tool_requires = ( + "cmake/[>=3.15]", + "doxygen/[>=1.8]", + ) requires = ( - "libcosim/0.10.2@osp/stable" - ) + "libcosim/0.10.3@osp/testing-bugfix_transitive-libs-boost", + ) - def set_version(self): - self.version = tools.load(path.join(self.recipe_folder, "version.txt")).strip() + # Exports + exports = "version.txt" + exports_sources = "*" - def imports(self): - binDir = os.path.join("output", str(self.settings.build_type).lower(), "bin") - self.copy("*.dll", dst=binDir, keep_path=False) - self.copy("*.pdb", dst=binDir, keep_path=False) + # Build steps + generators = "CMakeDeps", "CMakeToolchain" - def configure_cmake(self): - cmake = CMake(self) - cmake.definitions["LIBCOSIMC_USING_CONAN"] = "ON" - cmake.configure() - return cmake + def layout(self): + cmake_layout(self) def build(self): - cmake = self.configure_cmake() + cmake = CMake(self) + cmake.configure() cmake.build() cmake.build(target="doc") + if self._is_tests_enabled(): + env = VirtualRunEnv(self).environment() + env.define("CTEST_OUTPUT_ON_FAILURE", "ON") + with env.vars(self).apply(): + cmake.test() + # Packaging def package(self): - cmake = self.configure_cmake() + cmake = CMake(self) cmake.install() cmake.build(target="install-doc") def package_info(self): self.cpp_info.libs = [ "cosimc" ] + # Ensure that consumers use our CMake package configuration files + # rather than ones generated by Conan. + self.cpp_info.set_property("cmake_find_mode", "none") + self.cpp_info.builddirs.append(".") + + # Helper functions + def _is_tests_enabled(self): + return os.getenv("LIBCOSIMC_RUN_TESTS_ON_CONAN_BUILD", "False").lower() in ("true", "1") diff --git a/src/cosim.cpp b/src/cosim.cpp index bf7822f..ecbaa2b 100644 --- a/src/cosim.cpp +++ b/src/cosim.cpp @@ -21,13 +21,12 @@ #include #include -#include - #include #include #include #include #include +#include #include #include #include @@ -153,7 +152,7 @@ struct cosim_execution_s std::shared_ptr real_time_metrics; cosim::entity_index_maps entity_maps; std::thread t; - boost::fibers::future simulate_result; + std::future simulate_result; std::exception_ptr simulate_exception_ptr; std::atomic state; int error_code; @@ -559,7 +558,7 @@ int cosim_execution_start(cosim_execution* execution) } else { try { execution->state = COSIM_EXECUTION_RUNNING; - auto task = boost::fibers::packaged_task([execution]() { + auto task = std::packaged_task([execution]() { return execution->cpp_execution->simulate_until(std::nullopt); }); execution->simulate_result = task.get_future(); @@ -577,9 +576,11 @@ void execution_async_health_check(cosim_execution* execution) { if (execution->simulate_result.valid()) { const auto status = execution->simulate_result.wait_for(std::chrono::duration()); - if (boost::fibers::future_status::ready == status) { - if (auto ep = execution->simulate_result.get_exception_ptr()) { - execution->simulate_exception_ptr = ep; + if (status == std::future_status::ready) { + try { + execution->simulate_result.get(); + } catch (...) { + execution->simulate_exception_ptr = std::current_exception(); } } } @@ -593,7 +594,12 @@ int cosim_execution_stop(cosim_execution* execution) try { execution->cpp_execution->stop_simulation(); if (execution->t.joinable()) { - execution->simulate_result.get(); + if (execution->simulate_exception_ptr) { + std::rethrow_exception(execution->simulate_exception_ptr); + } + if (execution->simulate_result.valid()) { + execution->simulate_result.get(); + } execution->t.join(); } execution->state = COSIM_EXECUTION_STOPPED;