diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml index 04e36d75..e3762ce3 100644 --- a/.github/workflows/cpp.yml +++ b/.github/workflows/cpp.yml @@ -66,35 +66,33 @@ jobs: id: cache-build uses: actions/cache@v2 with: - path: PROPOSAL_BUILD + path: build key: ${{ runner.os }}-cache-build-${{ matrix.compiler }}-${{ github.sha }}-key - uses: actions/setup-python@v2 with: python-version: '3.8' - name: Install python dependencies - run: python -m pip install 'conan~=1.33' + run: python -m pip install conan - name: Initialize conan - if : steps.cache-conan.outputs.cache-hit != 'true' - run: conan profile new default --detect - - name: Update conan profile - if : ${{ matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-latest' }} - run: conan profile update settings.compiler.libcxx=libstdc++11 default - - name: Update conan profile - run: conan profile update settings.compiler.cppstd=14 default - - name: create build directory - if : steps.cache-build.outputs.cache-hit != 'true' - run: mkdir PROPOSAL_BUILD - - name: Install PROPOSAL dependencies - run: cd PROPOSAL_BUILD && conan install .. -o with_testing=True --build=missing - - name: Run build automatisation tool - run: cmake . -B PROPOSAL_BUILD -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_TESTING=TRUE - - name: Build lib + run: conan profile detect + - name: Prepare dependencies using conan + if : ${{ matrix.compiler != 'gcc-5' }} + run: conan install . --build=missing -o with_testing=True + - name: Prepare dependencies using conan (gcc5) + if : ${{ matrix.compiler == 'gcc-5' }} + run: conan install . --build=missing -o with_testing=True -s:b compiler.cppstd=gnu14 -s compiler.cppstd=gnu14 + - name: Call CMake if: ${{ matrix.os != 'windows-latest' }} - run: cmake --build PROPOSAL_BUILD - - name: Build lib + run: cmake . -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake + - name: Call CMake (Windows) if: ${{ matrix.os == 'windows-latest' }} - run: cmake --build PROPOSAL_BUILD --target ALL_BUILD --config Release - + run: cmake . -DCMAKE_TOOLCHAIN_FILE="build/conan_toolchain.cmake" -DCMAKE_POLICY_DEFAULT_CMP0091=NEW + - name: Build PROPOSAL + if: ${{ matrix.os != 'windows-latest' }} + run: cmake --build . -j2 + - name: Build PROPOSAL (Windows) + if: ${{ matrix.os != 'windows-latest' }} + run: cmake --build . -j2 --config Release test: runs-on: ${{ matrix.os }} needs: build @@ -115,7 +113,7 @@ jobs: compilerpp : "g++-5" - os : "windows-latest" env: - PROPOSAL_TEST_FILES: ${{ github.workspace }}/PROPOSAL_BUILD/tests/TestFiles + PROPOSAL_TEST_FILES: ${{ github.workspace }}/tests/TestFiles steps: - uses: actions/checkout@v2 - name: Cache conan @@ -129,7 +127,7 @@ jobs: id: cache-build uses: actions/cache@v2 with: - path: PROPOSAL_BUILD + path: build key: ${{ runner.os }}-cache-build-${{ matrix.compiler }}-${{ github.sha }}-key - name: Run tests - run: ctest --test-dir PROPOSAL_BUILD -j2 --verbose -E "(Brems.*Interpolant|Photo.*Interpolant|Epair.*Interpolant|Mupair.*Interpolant)" + run: ctest --test-dir tests -j2 --verbose -E "(Brems.*Interpolant|Photo.*Interpolant|Epair.*Interpolant|Mupair.*Interpolant)" \ No newline at end of file diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 9e8b3962..41b6f893 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -4,7 +4,12 @@ on: [push] jobs: build: - runs-on: "ubuntu-latest" + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os : "ubuntu-latest" + - os : "macos-latest" steps: - uses: actions/checkout@v2 - name: Cache conan @@ -12,17 +17,12 @@ jobs: uses: actions/cache@v2 with: path: ~/.conan - key: ${{ runner.os }}-cache-conan-gcc-${{ hashFiles('conanfile.py') }}-key + key: ${{ runner.os }}-cache-conan-${{ matrix.compiler }}-${{ hashFiles('conanfile.py') }}-key - name: Install conan - run: python -m pip install 'conan~=1.33' - - name: Initialize conan - if : steps.cache-conan.outputs.cache-hit != 'true' - run: conan profile new default --detect - - name: Update conan profile - run: conan profile update settings.compiler.libcxx=libstdc++11 default + run: python3 -m pip install conan - name: Install Python 🐍 distributions 📦 - run: python -m pip install . + run: python3 -m pip install . - name: Install pytest - run: python -m pip install pytest + run: python3 -m pip install pytest - name: run pytest - run: python -m pytest tests/python -v + run: python3 -m pytest tests/python -v diff --git a/CMakeLists.txt b/CMakeLists.txt index 37a47bd7..0bef1ab2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,10 +11,6 @@ project(PROPOSAL DESCRIPTION "The very best photon and lepton propagator." ) -if (EXISTS ${CMAKE_BINARY_DIR}/conan_paths.cmake) - include(${CMAKE_BINARY_DIR}/conan_paths.cmake) -endif() - if(NOT CMAKE_BUILD_TYPE) message(STATUS "No build type has been specified. Using default system build type. You may want to change this to" "'Release' if you are using PROPOSAL for production purposes.") diff --git a/INSTALL.md b/INSTALL.md index 409c12af..e75f23f5 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -8,7 +8,7 @@ All different installation approaches are going to be explained in the following For more detailed information about the specific building tools, see the listed documentations: -- [conan documentation](https://docs.conan.io/en/latest/) +- [conan documentation](https://docs.conan.io/2/) - [CMake documentation](https://cmake.org/cmake/help/latest/) - [pip documentation](https://pip.pypa.io/en/stable/) @@ -18,23 +18,23 @@ For more detailed information about the specific building tools, see the listed For this installation approach, all dependencies will be fetched by conan, meaning that you don't have to install them by yourself. If you have not installed conan yet, you can do so, for example: ```sh -$ pip install "conan~=1.33" +$ pip install "conan~=2.0" ``` Clone the repository and create a build directory ```sh $ git clone https://github.com/tudo-astroparticlephysics/PROPOSAL.git -$ cd PROPOSAL && mkdir build && cd build +$ cd PROPOSAL ``` -Use conan to prepare all dependencies. You can pass additional options to conan. +To prepare all dependencies, build PROPOSAL, and install PROPOSAL, simply use the command ```sh -$ conan install .. -o with_python=True # other optional dependencies +$ conan install . --build=missing -o with_python=True # other options ``` -The following options can be passed to `conan install`: +The following options can be passed to `conan build`: | Option. | default | Description | | -------------------- | ------- | --------------------------------------------- | @@ -42,23 +42,24 @@ The following options can be passed to `conan install`: | `with_testing` | False | Build TestFiles for Python. | | `with_documentation` | False | Build doxygen documentation of C++ code (WIP) | -Build and install PROPOSAL. You may require root privileges when installing, depending on the installation location: -```sh -$ conan build .. -``` +Next, call CMake, using the toolchain file created by conan: -To set the install location, use `-pf / --package-folder` option. -The default is `build/package`. -E.g. to install proposal into `$HOME/.local/proposal`: -``` -$ conan build .. -pf $HOME/.local/proposal +```sh +$ cd build +$ cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake ``` -This will also run the tests if `with_testing` is true. +Now make and install PROPOSAL + +```sh +$ cmake --build . -j4 +$ cmake --install . +``` +Per default, CMake installs PROPOSAL to `/usr/local`. To change this, specify the install directory when calling CMake, for example `cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_INSTALL_PREFIX=/path/to/dir`. -*Note:* As an alternative, you may create a local conan package and use it in your project. See the [conan documentation](https://docs.conan.io/en/latest/) for more information. +*Note:* As an alternative, you may create a local conan package and use it in your project. See the [conan documentation](https://docs.conan.io/2/) for more information. ## Building using pip (recommended for Python users) diff --git a/conanfile.py b/conanfile.py index 85ec2405..acc08c47 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,76 +1,128 @@ -from conans import ConanFile, CMake, tools import os +from conan import ConanFile +from conan.errors import ConanInvalidConfiguration +from conan.tools.build import check_min_cppstd +from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout +from conan.tools.files import copy, get, rmdir +from conan.tools.microsoft import is_msvc +from conan.tools.scm import Version + +required_conan_version = ">=1.53.0" class PROPOSALConan(ConanFile): name = "proposal" homepage = "https://github.com/tudo-astroparticlephysics/PROPOSAL" - license = "GNU Lesser General Public License v3.0" - description = "the very best lepton and photon propagator" + license = "LGPL-3.0" + package_type = "library" + url = "https://github.com/conan-io/conan-center-index" + description = "monte Carlo based lepton and photon propagator" topics = ("propagator", "lepton", "photon", "stochastic") + settings = "os", "compiler", "build_type", "arch" - exports_sources = "*" options = { "shared": [True, False], "fPIC": [True, False], - "with_testing": [True, False], "with_python": [True, False], + "with_testing": [True, False], "with_documentation": [True, False], } default_options = { "shared": False, "fPIC": True, + "with_python" : False, "with_testing": False, - "with_python": False, "with_documentation": False, } - generators = "cmake_find_package", "cmake_paths" - _cmake = None + + @property + def _min_cppstd(self): + return "14" + + @property + def _minimum_compilers_version(self): + return { + "Visual Studio": "15", + "msvc": "191", + "gcc": "5", + "clang": "5", + "apple-clang": "5", + } def config_options(self): if self.settings.os == "Windows": - del self.options.fPIC + self.options.rm_safe("fPIC") + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self) + self.folders.generators = "build" def requirements(self): - self.requires("cubicinterpolation/0.1.5") - self.requires("spdlog/1.10.0") - self.requires("nlohmann_json/3.9.1") + # cubicinterpolation: headers are transitively included, and function calls are made + # from implementation in headers (templates) + self.requires("cubicinterpolation/0.1.5", transitive_headers=True, transitive_libs=True) + # spdlog: requires transitive_libs due to direct calls to functionality from headers + self.requires("spdlog/1.11.0", transitive_headers=True, transitive_libs=True) + # nlohmann_json: public headers include json.hpp and json_fwd.hpp + self.requires("nlohmann_json/3.11.2", transitive_headers=True) if self.options.with_python: self.requires("pybind11/2.10.1") if self.options.with_testing: - self.requires("boost/1.78.0") + self.requires("boost/1.75.0") self.requires("gtest/1.11.0") if self.options.with_documentation: self.requires("doxygen/1.8.20") - def _configure_cmake(self): - if self._cmake: - return self._cmake - self._cmake = CMake(self) - self._cmake.definitions["BUILD_TESTING"] = self.options.with_testing - self._cmake.definitions["BUILD_PYTHON"] = self.options.with_python - self._cmake.definitions["BUILD_DOCUMENTATION"] = self.options.with_documentation - self._cmake.configure() - return self._cmake + + def validate(self): + if is_msvc(self) and self.options.shared: + raise ConanInvalidConfiguration( + "Can not build shared library on Visual Studio." + ) + if self.settings.compiler.get_safe("cppstd"): + check_min_cppstd(self, self._min_cppstd) + + minimum_version = self._minimum_compilers_version.get( + str(self.settings.compiler), False + ) + if minimum_version and Version(self.settings.compiler.version) < minimum_version: + raise ConanInvalidConfiguration( + f"{self.ref} requires C++{self._min_cppstd}, which your compiler does not support" + ) + + def source(self): + get(self, **self.conan_data["sources"][self.version], strip_root=True) def build(self): - cmake = self._configure_cmake() + cmake = CMake(self) + cmake.configure() cmake.build() - cmake.install() - if self.options.with_testing: - cmake.test() def package(self): - self.copy("LICENSE", dst="licenses") - cmake = self._configure_cmake() + copy(self, "LICENSE.md", self.source_folder, os.path.join(self.package_folder, "licenses")) + cmake = CMake(self) cmake.install() - tools.rmdir(os.path.join(self.package_folder, "lib", "cmake")) + rmdir(self, os.path.join(self.package_folder, "lib", "cmake")) + + def generate(self): + tc = CMakeToolchain(self) + tc.variables["BUILD_TESTING"] = self.options.with_testing + tc.variables["BUILD_PYTHON"] = self.options.with_python + tc.variables["BUILD_DOCUMENTATION"] = self.options.with_documentation + tc.variables["CMAKE_BUILD_TYPE"] = "Release" # set as default + tc.generate() + deps = CMakeDeps(self) + deps.generate() def package_info(self): - self.cpp_info.names["cmake_find_package"] = "PROPOSAL" + self.cpp_info.set_property("cmake_file_name", "PROPOSAL") + self.cpp_info.set_property("cmake_target_name", "PROPOSAL::PROPOSAL") self.cpp_info.libs = ["PROPOSAL"] - self.cpp_info.requires = [ - "cubicinterpolation::CubicInterpolation", - "spdlog::spdlog", - "nlohmann_json::nlohmann_json", - ] + + # TODO: to remove in conan v2 once cmake_find_package_* generators removed + self.cpp_info.names["cmake_find_package"] = "PROPOSAL" + self.cpp_info.names["cmake_find_package_multi"] = "PROPOSAL" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index aa349d55..246260a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,6 @@ requires = [ "setuptools>=45", "wheel", "cmake>=3.16", - "conan~=1.33", + "conan>=2.0.0", ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 37dac068..0442b61f 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,7 @@ def get_cmake(): if ret.returncode == 0: return exe - raise OSError("You need cmake >= 3.16") - + raise OSError("You need cmake >= 3.23") def exists_conan_default_file(): profiles = sp.check_output(["conan", "profile", "list"], encoding="UTF-8").split() @@ -41,36 +40,15 @@ def exists_conan_default_file(): return True return False - -def create_conan_profile(name): - cmd = ["conan", "profile", "new", f"{name}", "--detect"] +def create_conan_profile(): + cmd = ["conan", "profile", "detect"] r = sp.run(cmd) if r.returncode != 0: raise RuntimeError( - "conan was not able to create a new profile named {name}." + "conan was not able to create a new default profile." ) -def is_old_libcxx(): - """ if we are on gcc, we might be using an old library ABI """ - - cmd = ["conan", "profile", "get", "settings.compiler", "default"] - r = sp.check_output(cmd, encoding="UTF-8") - compiler = r.split()[0] - - if compiler != "gcc": - return False - - cmd = ["conan", "profile", "get", "settings.compiler.libcxx", "default"] - r = sp.check_output(cmd, encoding="UTF-8") - libcxx = r.split()[0] - - if libcxx == "libstdc++11": - return False - - return True - - class CMakeExtension(Extension): def __init__(self, name, source_dir=None, target=None, **kwargs): if source_dir is None: @@ -99,14 +77,13 @@ def build_cmake(self, ext): cmake = get_cmake() rpath = '@loader_path' if sys.platform == 'darwin' else '$ORIGIN' - CMAKE_CXX_FLAGS = "" if not os.getenv("NO_CONAN", False): print( "Using conan to install dependencies. Set environment variable NO_CONAN to skip conan." ) if not exists_conan_default_file(): - create_conan_profile("default") + create_conan_profile() conan_call = [ 'conan', @@ -117,24 +94,18 @@ def build_cmake(self, ext): '--build=missing' ] sp.run(conan_call, cwd=self.build_temp, check=True) - if is_old_libcxx(): - CMAKE_CXX_FLAGS = '-DCMAKE_CXX_FLAGS="-D_GLIBCXX_USE_CXX11_ABI=OFF"' cmake_call = [ cmake, + ext.source_dir, + '-DCMAKE_TOOLCHAIN_FILE={}/build/conan_toolchain.cmake'.format(ext.source_dir), '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, '-DCMAKE_BUILD_TYPE=' + cfg, - '-DBUILD_TESTING=OFF', - '-DBUILD_PYTHON=ON', - '-DCMAKE_POSITION_INDEPENDENT_CODE=TRUE', - '-DBUILD_EXAMPLE=OFF', '-DPython_EXECUTABLE=' + sys.executable, '-DCMAKE_INSTALL_RPATH={}'.format(rpath), '-DCMAKE_BUILD_WITH_INSTALL_RPATH:BOOL=ON', '-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=OFF', ] - if CMAKE_CXX_FLAGS: - cmake_call.append(CMAKE_CXX_FLAGS) sp.run(cmake_call, cwd=self.build_temp, check=True) build_call = [ cmake,