projectchrono / chrono

High-performance C++ library for multiphysics and multibody dynamics simulations
http://projectchrono.org
BSD 3-Clause "New" or "Revised" License
2.24k stars 470 forks source link

Create Conan package [Feature Request] #270

Open jellespijker opened 4 years ago

jellespijker commented 4 years ago

Feature Request I think you guys are doing a great job and I really like to use Project Chrono in a variety of projects, But not every project of mine requires the same options. Compiling and maintaining different builds of dependencies for different projects can be difficult and time consuming and lets be honest who likes building and compiling dependencies.

I came across Conan (https://conan.io/) a year ago, which is a decentralized and multi-platform package manager, which allows me and consumers of my projects, to manage and share native binaries. This allows me to import dependencies such as Project Chrono for a new project with just a few lines in my CMake file or with a Python script. The beauty is that you have only to compile it once for a certain set of options and that these binaries can be uploaded to a server (public or private). If I have a different project with the same options, the binaries can be retrieved, saving a lot of time. The same goes for all dependencies of Project Chrono, these are download or compiled as needed.

I have create my own repository which allows me use Project Chrono with Conan ( https://github.com/jellespijker/conan-projectchrono ), but this project is only tested on one platform (Linux - arch-based) and not every option and optional dependencies are included. I think it would be great if you guys provide support for a Conan package. This would i.m.o. greatly increase the ease with which I and others can use Project Chrono in our projects.

from conans import ConanFile, CMake, tools
import os

class ProjectChronoConan(ConanFile):
    name = "projectchrono"
    version = "5.0.1"
    description = "C++ library for multi-physics simulation"
    topics = ("chronoengine", "project chrono", "multi-physics simulation", "DEM", "FSI", "vehicle", "simulation")
    url = "https://github.com/jellespijker/conan-projectchrono"
    homepage = "https://github.com/projectchrono/chrono"
    license = "AGPL3"
    # exports_sources = ["CMakeLists.txt", "patches/*.patch"]
    generators = "cmake", "cmake_find_package", "cmake_paths"

    settings = "os", "compiler", "build_type", "arch"
    options = {"shared": [True, False],
               "fPIC": [True, False],
               "openMP": [True, False],
               "TBB": [True, False],
               "HDF5": [True, False],
               "benchmarking": [True, False],
               "demos": [True, False],
               "tests": [True, False],
               "unit_cascade": [True, False],  # Not yet implemented
               "module_cosimulation": [True, False],
               "module_distributed": [True, False],
               "module_irrlicht": [True, False],
               "module_matlab": [True, False],
               "module_mkl": [True, False],  #  Not yet implemented
               "module_mumps": [True, False],  # Not yet implemented
               "module_parallel": [True, False],
               "module_opengl": [True, False],
               "module_ogre": [True, False],
               "module_postprocess": [True, False],
               "module_python": [True, False],
               "module_vehicle": [True, False],
               "module_fsi": [True, False],
               "use_bullet_double": [True, False],
               "module_granular": [True, False],
               "use_parallel_cuda": [True, False],  # Not yet implemented
               "use_parallel_double": [True, False]}
    default_options = {"shared": True,
                       "fPIC": True,
                       "openMP": False,
                       "TBB": True,
                       "HDF5": False,
                       "benchmarking": False,
                       "demos": False,
                       "tests": False,
                       "unit_cascade": False,
                       "module_cosimulation": False,
                       "module_distributed": False,
                       "module_irrlicht": False,
                       "module_matlab": False,
                       "module_mkl": False,
                       "module_mumps": False,
                       "module_parallel": False,
                       "module_opengl": False,
                       "module_ogre": False,
                       "module_postprocess": False,
                       "module_python": False,
                       "module_vehicle": False,
                       "module_fsi": False,
                       "use_bullet_double": False,
                       "module_granular": False,
                       "use_parallel_cuda": False,
                       "use_parallel_double": True}

    _source_subfolder = "src"
    _build_folder = "build"
    _cmake = None

    scm = {
        "type": "git",
        "subfolder":_source_subfolder,
        "url": "https://github.com/projectchrono/chrono.git",
        "revision": version,
        "submodule": "recursive"
    }

    def source(self):
        tools.replace_in_file("src/CMakeLists.txt", "project(Chrono VERSION ${CHRONO_VERSION_MAJOR}.${CHRONO_VERSION_MINOR}.${CHRONO_VERSION_PATCH})",
                              '''project(Chrono VERSION ${CHRONO_VERSION_MAJOR}.${CHRONO_VERSION_MINOR}.${CHRONO_VERSION_PATCH})
include(${CMAKE_BINARY_DIR}/../conanbuildinfo.cmake)
conan_basic_setup()
include_directories(CONAN_INCLUDE_DIRS_IRRLICHT)''')

    def config_options(self):
        if self.settings.os == "Windows":
            del self.options.fPIC

    def requirements(self):
        if self.options.openMP:  # and str(self.settings.compiler) == 'clang':
            self.requires.add("llvm-openmp/10.0.0")
        if self.options.TBB:
            self.requires.add("tbb/2020.1")
        self.requires.add("zlib/1.2.11")
        self.requires.add("openmpi/3.1.2@jellespijker/testing")
        self.requires.add("eigen/3.3.7")
        if self.options.HDF5:
            self.requires.add("hdf5/1.12.0")
        if self.options.module_irrlicht:
            self.requires.add("irrlicht/1.8.4@jellespijker/testing")
            self.requires.add("openssl/1.1.1g")
        if self.options.module_mumps:
            # self.requires.add("mumps")
            self.requires.add("openblas/0.3.10")
        if self.options.module_opengl:
            self.requires.add("opengl/system")
            self.requires.add("glm/0.9.9.8")
            self.requires.add("glew/2.1.0")
            self.requires.add("glfw/3.3.2")
        if self.options.module_parallel:
            self.requires.add("thrust/1.9.5")
            self.requires.add("blaze/3.7")
        if self.options.module_python:
            self.requires.add("swig/4.0.1")

    def _configure_cmake(self):
        if not self._cmake:
            self._cmake = CMake(self)
            self._cmake.definitions["BUILD_DEMOS"] = self.options.demos
            self._cmake.definitions["BUILD_BENCHMARKING"] = self.options.benchmarking
            self._cmake.definitions["ENABLE_OPENMP"] = self.options.openMP
            self._cmake.definitions["ENABLE_TBB"] = self.options.TBB
            self._cmake.definitions["ENABLE_HDF5"] = self.options.HDF5
            self._cmake.definitions["BUILD_TESTING"] = self.options.tests
            self._cmake.definitions["ENABLE_UNIT_CASCADE"] = self.options.unit_cascade
            self._cmake.definitions["ENABLE_MODULE_COSIMULATION"] = self.options.module_cosimulation
            self._cmake.definitions["ENABLE_MODULE_DISTRIBUTED"] = self.options.module_distributed
            self._cmake.definitions["ENABLE_MODULE_IRRLICHT"] = self.options.module_irrlicht
            self._cmake.definitions["ENABLE_MODULE_MATLAB"] = self.options.module_matlab
            self._cmake.definitions["ENABLE_MODULE_MKL"] = self.options.module_mkl
            self._cmake.definitions["ENABLE_MODULE_MUMPS"] = self.options.module_mumps
            self._cmake.definitions["ENABLE_MODULE_PARALLEL"] = self.options.module_parallel
            self._cmake.definitions["ENABLE_MODULE_OPENGL"] = self.options.module_opengl
            self._cmake.definitions["ENABLE_MODULE_OGRE"] = self.options.module_ogre
            self._cmake.definitions["ENABLE_MODULE_POSTPROCESS"] = self.options.module_postprocess
            self._cmake.definitions["ENABLE_MODULE_PYTHON"] = self.options.module_python
            self._cmake.definitions["ENABLE_MODULE_VEHICLE"] = self.options.module_vehicle
            self._cmake.definitions["ENABLE_MODULE_FSI"] = self.options.module_fsi
            self._cmake.definitions["ENABLE_MODULE_GRANULAR"] = self.options.module_granular
        self._cmake.configure(source_folder=self._source_subfolder, build_folder=self._build_folder)
        return self._cmake

    def build(self):
        cmake = self._configure_cmake()
        cmake.build()

    def package(self):
        self.copy(pattern="LICENSE", dst="licenses", src=self._source_subfolder)
        cmake = self._configure_cmake()
        cmake.install()

    def package_info(self):
        self.cpp_info.libs = tools.collect_libs(self)

Usage I use the Conan Wrapper in a project of mine called FantasticFuckinFusion ( https://github.com/jellespijker/FantasticFuckinFusion ) which is a 3D printer simulator based on Qt5 3D and Project Chrono. Two big projects which can be challenging to build from source, but using Conan it is much easier. Look at the snippets of code in the CmakeLists.txt below

Main CMakeLists.txt

....

# Set up some extra Conan dependencies based on our needs before loading Conan
set(CONAN_EXTRA_REQUIRES
        zlib/1.2.11@conan/stable
        freetype/2.10.1
        fontconfig/2.13.91@jellespijker/testing
        qt/5.15.1@bincrafters/stable
        projectchrono/5.0.1@jellespijker/testing)
set(CONAN_EXTRA_OPTIONS
        projectchrono:module_irrlicht=False
        qt:shared=False
        qt:qtsvg=True
        qt:qt3d=True
        qt:qtimageformats=True
        qt:qtquick3d=True)

include(cmake/Conan.cmake)
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets 3DCore 3DExtras 3DRender 3DInput)
set(QT_NO_BEARERMANAGEMENT ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

run_conan()

...

CMakeLists.txt for my FFF lib

...

target_compile_definitions(
        fff
        PRIVATE
            ${CONAN_COMPILE_DEFINITIONS_PROJECTCHRONO}
            ${CONAN_COMPILE_DEFINITIONS_QT})

target_include_directories(
        fff
        PRIVATE
            ${CONAN_INCLUDE_DIRS_PROJECTCHRONO}
            ${CONAN_INCLUDE_DIRS_QT})

target_link_libraries(
        fff
        PUBLIC
            CONAN_PKG::projectchrono
            CONAN_PKG::qt
            Qt5::Core
            Qt5::Gui
            Qt5::Widgets
            Qt5::3DCore
            Qt5::3DExtras
            Qt5::3DRender
            Qt5::3DInput
        PRIVATE
            project_options
            project_warnings)

...

Conan.cmake ( copied from the C++ template project by Jason Turner https://github.com/lefticus/cpp_starter_project )

macro(run_conan)
    # Download automatically, you can also just copy the conan.cmake file
    if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
        message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
        file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/v0.15/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake")
    endif()

    include(${CMAKE_BINARY_DIR}/conan.cmake)

    conan_add_remote(
            NAME
            bincrafters
            URL
            https://api.bintray.com/conan/bincrafters/public-conan)
    conan_add_remote(
            NAME
            conan-center
            URL
            https://conan.bintray.com)
    conan_add_remote(
            NAME
            FFF
            URL
            https://api.bintray.com/conan/jellespijker/FantasticFuckinFusion)

    conan_cmake_run(
            REQUIRES
            ${CONAN_EXTRA_REQUIRES}
            catch2/2.11.0
            docopt.cpp/0.6.2
            fmt/6.2.0
            spdlog/1.5.0
            OPTIONS
            ${CONAN_EXTRA_OPTIONS}
            BASIC_SETUP
            CMAKE_TARGETS # individual targets to link to
            SETTINGS
            compiler.cppstd=17
            build_type=Release
            BUILD
            missing)
endmacro()
jellespijker commented 1 year ago

I'm looking into using Project Chrono as a simulator for the motion system of a 3D printer, such that simulate the output of Cura, one of the more popular open source slicers for 3d printers.

Since Cura and CuraEngine build-systems rely heavily on Conan I'm planning on adding Project chrono to the CCI (Conan-Center-Index) such that we can use it downstream in one of our projects. See newly opened issue in CCI: https://github.com/conan-io/conan-center-index/issues/18561

PR to follow in the CCI repo in due-time, working branch: https://github.com/Ultimaker/conan-center-index/tree/GH-18561_projectchrono