diff --git a/recipes/openblas/all/conandata.yml b/recipes/openblas/all/conandata.yml index 85e4fee5631f1..65bcb5a0a3da7 100644 --- a/recipes/openblas/all/conandata.yml +++ b/recipes/openblas/all/conandata.yml @@ -1,4 +1,7 @@ sources: + "0.3.28": + url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.28.tar.gz" + sha256: "f1003466ad074e9b0c8d421a204121100b0751c96fc6fcf3d1456bd12f8a00a1" "0.3.27": url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.27.tar.gz" sha256: "aa2d68b1564fe2b13bc292672608e9cdeeeb6dc34995512e65c3b10f4599e897" @@ -11,21 +14,24 @@ sources: "0.3.24": url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.24.tar.gz" sha256: "ceadc5065da97bd92404cac7254da66cc6eb192679cf1002098688978d4d5132" - "0.3.20": - url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.20.tar.gz" - sha256: "8495c9affc536253648e942908e88e097f2ec7753ede55aca52e5dead3029e3c" - "0.3.17": - url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.17.tar.gz" - sha256: "df2934fa33d04fd84d839ca698280df55c690c86a5a1133b3f7266fce1de279f" - "0.3.15": - url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.15.tar.gz" - sha256: "30a99dec977594b387a17f49904523e6bc8dd88bd247266e83485803759e4bbe" - "0.3.13": - url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.13.tar.gz" - sha256: "79197543b17cc314b7e43f7a33148c308b0807cd6381ee77f77e15acf3e6459e" - "0.3.12": - url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.12.tar.gz" - sha256: "65a7d3a4010a4e3bd5c0baa41a234797cd3a1735449a4a5902129152601dc57b" - "0.3.10": - url: "https://github.com/xianyi/OpenBLAS/archive/v0.3.10.tar.gz" - sha256: "0484d275f87e9b8641ff2eecaa9df2830cbe276ac79ad80494822721de6e1693" +patches: + "0.3.28": + - patch_file: "patches/0001-use-openmp-target.patch" + patch_description: "Use OpenMP target instead of flags for llvm-openmp" + patch_type: "conan" + "0.3.27": + - patch_file: "patches/0001-use-openmp-target.patch" + patch_description: "Use OpenMP target instead of flags for llvm-openmp" + patch_type: "conan" + "0.3.26": + - patch_file: "patches/0001-use-openmp-target.patch" + patch_description: "Use OpenMP target instead of flags for llvm-openmp" + patch_type: "conan" + "0.3.25": + - patch_file: "patches/0001-use-openmp-target.patch" + patch_description: "Use OpenMP target instead of flags for llvm-openmp" + patch_type: "conan" + "0.3.24": + - patch_file: "patches/0001-use-openmp-target.patch" + patch_description: "Use OpenMP target instead of flags for llvm-openmp" + patch_type: "conan" diff --git a/recipes/openblas/all/conanfile.py b/recipes/openblas/all/conanfile.py index 6fcc5d83048b9..fb5fa52fd3812 100644 --- a/recipes/openblas/all/conanfile.py +++ b/recipes/openblas/all/conanfile.py @@ -2,12 +2,11 @@ from conan.errors import ConanInvalidConfiguration from conan.tools.apple import fix_apple_shared_install_name from conan.tools.build import cross_building -from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout -from conan.tools.files import copy, get, replace_in_file, rmdir +from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout, CMakeDeps +from conan.tools.env import VirtualBuildEnv +from conan.tools.files import copy, get, rmdir, export_conandata_patches, apply_conandata_patches, save from conan.tools.microsoft import is_msvc_static_runtime, is_msvc -from conan.tools.scm import Version import os -import textwrap required_conan_version = ">=1.53.0" @@ -61,7 +60,7 @@ class OpenblasConan(ConanFile): name = "openblas" - description = "An optimized BLAS library based on GotoBLAS2 1.13 BSD version" + description = "An optimized BLAS library based on GotoBLAS2, with LAPACK" license = "BSD-3-Clause" url = "https://github.com/conan-io/conan-center-index" homepage = "https://www.openblas.net" @@ -71,30 +70,45 @@ class OpenblasConan(ConanFile): options = { "shared": [True, False], "fPIC": [True, False], + "ilp64": [True, False], "build_lapack": [True, False], "build_relapack": [True, False], + "build_bfloat16": [True, False], + "use_openmp": [True, False], "use_thread": [True, False], "use_locking": [True, False], "dynamic_arch": [True, False], - "target": [None] + available_openblas_targets + "target": [None] + available_openblas_targets, + "max_threads": ["auto", "ANY"], + "max_omp_parallel": ["ANY"], } default_options = { "shared": False, "fPIC": True, + "ilp64": False, "build_lapack": True, - "build_relapack": False, + "build_relapack": True, + "build_bfloat16": False, + "use_openmp": True, "use_thread": True, "use_locking": True, "dynamic_arch": False, "target": None, + "max_threads": 128, + "max_omp_parallel": 1, } options_description = { + "ilp64": "Build with ILP64 interface instead of LP64 (incompatible with the standard API)", "build_lapack": "Build LAPACK and LAPACKE", "build_relapack": "Build with ReLAPACK (recursive implementation of several LAPACK functions on top of standard LAPACK)", + "build_bfloat16": "Build with bfloat16 support", + "use_openmp": "Enable OpenMP support", "use_thread": "Enable threads support", "use_locking": "Use locks even in single-threaded builds to make them callable from multiple threads", "dynamic_arch": "Include support for multiple CPU targets, with automatic selection at runtime (x86/x86_64, aarch64 or ppc only)", "target": "OpenBLAS TARGET variable (see TargetList.txt)", + "max_threads": "The maximum number of parallel threads you expect to need (defaults to the number of cores in the build cpu)", + "max_omp_parallel": "Number of OpenMP instances that your code may use for parallel calls into OpenBLAS", } short_paths = True @@ -105,47 +119,56 @@ def _fortran_compiler(self): return comp_exe["fortran"] return None + def export_sources(self): + export_conandata_patches(self) + def config_options(self): if self.settings.os == "Windows": del self.options.fPIC - # When no Fortran compiler is available, OpenBLAS builds LAPACK from an f2c-converted copy of LAPACK unless the NO_LAPACK option is specified. - # This is not available before v0.3.21. - if Version(self.version) < "0.3.21": - self.options.build_lapack = False - self.options.build_relapack = False def configure(self): if self.options.shared: self.options.rm_safe("fPIC") + if not self.options.build_lapack: + del self.options.build_relapack + else: + self.options.build_relapack = self.settings.compiler in ["gcc", "clang"] + if not self.options.use_thread: + del self.options.max_threads + if not self.options.use_openmp: + del self.options.max_omp_parallel # When cross-compiling, OpenBLAS requires explicitly setting TARGET if cross_building(self, skip_x64_x86=True) and not self.options.target: # Try inferring the target from settings.arch target = conan_arch_to_openblas_target.get(str(self.settings.arch)) if target: - self.output.warning(f'Setting OpenBLAS TARGET={target} based on settings.arch. This may result in suboptimal performance. Set the "{self.name}/*:target=XXX" option to silence this warning.') + self.output.warning( + f"Setting OpenBLAS TARGET={target} based on settings.arch. " + "This may result in suboptimal performance. " + f'Set the "{self.name}/*:target=XXX" option to silence this warning.' + ) self.options.target = target + def requirements(self): + if self.options.use_openmp and self.settings.compiler in ["clang", "apple-clang"]: + self.requires("llvm-openmp/17.0.6") + def validate(self): - if Version(self.version) < "0.3.24" and self.settings.arch == "armv8": - # OpenBLAS fails to detect the appropriate target architecture for armv8 for versions < 0.3.24, as it matches the 32 bit variant instead of 64. - # This was fixed in https://github.com/OpenMathLib/OpenBLAS/pull/4142, which was introduced in 0.3.24. - # This would be a reasonably trivial hotfix to backport. - raise ConanInvalidConfiguration("armv8 builds are not currently supported for versions lower than 0.3.24. Contributions to support this are welcome.") - - if self.options.build_relapack: - if not self.options.build_lapack: - raise ConanInvalidConfiguration(f'"{self.name}/*:build_relapack=True" option requires "{self.name}/*:build_lapack=True"') - if self.settings.compiler not in ["gcc", "clang"]: - # ld: unknown option: --allow-multiple-definition on apple-clang - raise ConanInvalidConfiguration(f'"{self.name}/*:build_relapack=True" option is only supported for GCC and Clang') + if self.options.get_safe("build_relapack") and self.settings.compiler not in ["gcc", "clang"]: + # ld: unknown option: --allow-multiple-definition on apple-clang + raise ConanInvalidConfiguration(f'"{self.name}/*:build_relapack=True" option is only supported for GCC and Clang') + if self.settings.os == "Android" and self.options.build_lapack: + if int(self.settings.os.api_level.value) < 23: + # All basic complex math functions are only available since API level 23. + raise ConanInvalidConfiguration("build_lapack=True requires Android API level 23 or higher for complex math support") - def validate_build(self): - if Version(self.version) < "0.3.22" and cross_building(self, skip_x64_x86=True): - # OpenBLAS CMake builds did not support some of the cross-compilation targets in 0.3.20/21 and earlier. - # This was fixed in https://github.com/OpenMathLib/OpenBLAS/pull/3714 and https://github.com/OpenMathLib/OpenBLAS/pull/3958 - raise ConanInvalidConfiguration(f"Cross-building is not supported for {self.name}/0.3.21 and earlier.") + def build_requirements(self): + if self.options.use_openmp: + # Required for LINK_LANGUAGE generator expression + self.tool_requires("cmake/[>=3.18 <4]") + def validate_build(self): # If we're cross-compiling, and the user didn't provide the target, and # we couldn't infer the target from settings.arch, fail if cross_building(self, skip_x64_x86=True) and not self.options.target: @@ -153,35 +176,43 @@ def validate_build(self): def source(self): get(self, **self.conan_data["sources"][self.version], strip_root=True) + apply_conandata_patches(self) def layout(self): cmake_layout(self, src_folder="src") def generate(self): + VirtualBuildEnv(self).generate() + tc = CMakeToolchain(self) tc.variables["BUILD_STATIC_LIBS"] = not self.options.shared tc.variables["BUILD_SHARED_LIBS"] = self.options.shared tc.variables["BUILD_TESTING"] = False tc.variables["NOFORTRAN"] = not self.options.build_lapack - # This checks explicit user-specified fortran compiler if self.options.build_lapack and not self._fortran_compiler: - if Version(self.version) < "0.3.21": - self.output.warning("Building with LAPACK support requires a Fortran compiler.") - else: - tc.variables["C_LAPACK"] = True - tc.variables["NOFORTRAN"] = True - self.output.info("Building LAPACK without a Fortran compiler") + tc.variables["C_LAPACK"] = True + tc.variables["NOFORTRAN"] = True + self.output.info("Building LAPACK without a Fortran compiler") tc.variables["BUILD_WITHOUT_LAPACK"] = not self.options.build_lapack - tc.variables["BUILD_RELAPACK"] = self.options.build_relapack - + tc.variables["BUILD_RELAPACK"] = self.options.get_safe("build_relapack", False) + tc.variables["BUILD_BFLOAT16"] = self.options.build_bfloat16 + tc.variables["INTERFACE64"] = self.options.ilp64 tc.variables["DYNAMIC_ARCH"] = self.options.dynamic_arch + tc.variables["USE_OPENMP"] = self.options.use_openmp tc.variables["USE_THREAD"] = self.options.use_thread tc.variables["USE_LOCKING"] = self.options.use_locking + if self.options.get_safe("max_threads", "auto") != "auto": + tc.variables["NUM_PARALLEL"] = self.options.max_threads + if self.options.get_safe("max_omp_parallel"): + tc.variables["NUM_THREADS"] = self.options.max_omp_parallel tc.variables["MSVC_STATIC_CRT"] = is_msvc_static_runtime(self) + # Needed for $<$:OpenMP::OpenMP_C> to work correctly + tc.cache_variables["CMAKE_POLICY_DEFAULT_CMP0022"] = "NEW" + # This is a workaround to add the libm dependency on linux, # which is required to successfully compile on older gcc versions. tc.variables["ANDROID"] = self.settings.os in ["Linux", "Android"] @@ -192,41 +223,17 @@ def generate(self): tc.cache_variables["CMAKE_POLICY_DEFAULT_CMP0077"] = "NEW" tc.generate() + deps = CMakeDeps(self) + deps.generate() + def _patch_sources(self): - if Version(self.version) <= "0.3.15": - replace_in_file(self, os.path.join(self.source_folder, "cmake", "utils.cmake"), - "set(obj_defines ${defines_in})", textwrap.dedent("""\ - set(obj_defines ${defines_in}) - - list(FIND obj_defines "RC" def_idx) - if (${def_idx} GREATER -1) - list(REMOVE_ITEM obj_defines "RC") - list(APPEND obj_defines "RC=RC") - endif () - list(FIND obj_defines "CR" def_idx) - if (${def_idx} GREATER -1) - list(REMOVE_ITEM obj_defines "CR") - list(APPEND obj_defines "CR=CR") - endif ()""")) - if Version(self.version) < "0.3.21": - f_check_cmake = os.path.join(self.source_folder, "cmake", "f_check.cmake") - if Version(self.version) >= "0.3.12": - replace_in_file(self, f_check_cmake, - 'message(STATUS "No Fortran compiler found, can build only BLAS but not LAPACK")', - 'message(FATAL_ERROR "No Fortran compiler found. Cannot build with LAPACK.")') - else: - replace_in_file(self, f_check_cmake, - "enable_language(Fortran)", - textwrap.dedent("""\ - include(CheckLanguage) - check_language(Fortran) - if(CMAKE_Fortran_COMPILER) - enable_language(Fortran) - else() - message(FATAL_ERROR "No Fortran compiler found. Cannot build with LAPACK.") - set (NOFORTRAN 1) - set (NO_LAPACK 1) - endif()""")) + # Disable test subdirs. + # ctest also otherwise fails to compile on Android API 22 and earlier due to incomplete complex support. + save(self, os.path.join(self.source_folder, "cpp_thread_test", "CMakeLists.txt"), "") + save(self, os.path.join(self.source_folder, "ctest", "CMakeLists.txt"), "") + save(self, os.path.join(self.source_folder, "test", "CMakeLists.txt"), "") + save(self, os.path.join(self.source_folder, "utest", "CMakeLists.txt"), "") + save(self, os.path.join(self.source_folder, "lapack-netlib", "TESTING", "CMakeLists.txt"), "") def build(self): self._patch_sources() @@ -245,23 +252,25 @@ def package(self): @property def _lib_name(self): + name = "openblas" + if self.options.ilp64: + name += "_64" if self.options.shared and self.settings.build_type == "Debug" and not is_msvc(self): - return "openblas_d" - return "openblas" + name += "_d" + return name + + @property + def _64bit(self): + return "64" if self.options.ilp64 else "" def package_info(self): - # CMake config file: - # - OpenBLAS always has one and only one of these components: openmp, pthread or serial. - # - Whatever if this component is requested or not, official CMake imported target is always OpenBLAS::OpenBLAS - # - TODO: add openmp component when implemented in this recipe - self.cpp_info.set_property("cmake_file_name", "OpenBLAS") + self.cpp_info.set_property("cmake_file_name", f"OpenBLAS{self._64bit}") self.cpp_info.set_property("cmake_target_name", "OpenBLAS::OpenBLAS") - self.cpp_info.set_property("pkg_config_name", "openblas") - # 'pthread' causes issues without namespace - cmake_component_name = "pthread" if self.options.use_thread else "serial" # TODO: how to model this in CMakeDeps? - self.cpp_info.components["openblas_component"].set_property("cmake_target_name", f"OpenBLAS::{cmake_component_name}") - self.cpp_info.components["openblas_component"].set_property("pkg_config_name", "openblas") - self.cpp_info.components["openblas_component"].includedirs.append(os.path.join("include", "openblas")) + if self.options.ilp64: + self.cpp_info.set_property("cmake_target_aliases", ["OpenBLAS64::OpenBLAS"]) + self.cpp_info.set_property("pkg_config_name", f"openblas{self._64bit}") + self.cpp_info.components["openblas_component"].set_property("pkg_config_name", f"openblas{self._64bit}") + self.cpp_info.components["openblas_component"].includedirs.append(os.path.join("include", f"openblas{self._64bit}")) self.cpp_info.components["openblas_component"].libs = [self._lib_name] if self.settings.os in ["Linux", "FreeBSD"]: self.cpp_info.components["openblas_component"].system_libs.append("m") @@ -269,13 +278,41 @@ def package_info(self): self.cpp_info.components["openblas_component"].system_libs.append("pthread") if self.options.build_lapack and self._fortran_compiler: self.cpp_info.components["openblas_component"].system_libs.append("gfortran") + if self.options.use_openmp: + self.cpp_info.components["openblas_component"].requires.append("openmp::openmp") + + # OpenBLAS always has one and only one of these components defined: openmp, pthread or serial. + if self.options.use_openmp: + component = "openmp" + elif self.options.use_thread: + component = "pthread" + else: + component = "serial" + self.cpp_info.components[component].set_property("cmake_target_name", f"OpenBLAS::{component}") + self.cpp_info.components[component].requires = ["openblas_component"] + + if self.options.use_openmp: + if self.options.use_openmp and self.settings.compiler in ["clang", "apple-clang"]: + self.cpp_info.components["openblas_component"].requires.append("llvm-openmp::llvm-openmp") + else: + if is_msvc(self): + openmp_flags = ["-openmp"] + elif self.settings.compiler == "gcc": + openmp_flags = ["-fopenmp"] + elif self.settings.compiler == "intel-cc": + openmp_flags = ["-Qopenmp"] + self.cpp_info.exelinkflags.extend(openmp_flags) + self.cpp_info.sharedlinkflags.extend(openmp_flags) + + # OpenBLAS always has one and only one of these components defined: openmp, pthread or serial. + if self.options.use_openmp: + component = "openmp" + elif self.options.use_thread: + component = "pthread" + else: + component = "serial" + self.cpp_info.components[component].set_property("cmake_target_name", f"OpenBLAS::{component}") + self.cpp_info.components[component].requires = ["openblas_component"] self.buildenv_info.define_path("OpenBLAS_HOME", self.package_folder) self.runenv_info.define_path("OpenBLAS_HOME", self.package_folder) - - # TODO: to remove in conan v2 once cmake_find_package_* generators removed - self.cpp_info.names["cmake_find_package"] = "OpenBLAS" - self.cpp_info.names["cmake_find_package_multi"] = "OpenBLAS" - self.cpp_info.components["openblas_component"].names["cmake_find_package"] = cmake_component_name - self.cpp_info.components["openblas_component"].names["cmake_find_package_multi"] = cmake_component_name - self.env_info.OpenBLAS_HOME = self.package_folder diff --git a/recipes/openblas/all/patches/0001-use-openmp-target.patch b/recipes/openblas/all/patches/0001-use-openmp-target.patch new file mode 100644 index 0000000000000..27eec40c846ea --- /dev/null +++ b/recipes/openblas/all/patches/0001-use-openmp-target.patch @@ -0,0 +1,28 @@ +--- a/cmake/arch.cmake ++++ b/cmake/arch.cmake +@@ -35,9 +35,22 @@ if (USE_OPENMP) + # USE_SIMPLE_THREADED_LEVEL3 = 1 + # NO_AFFINITY = 1 + find_package(OpenMP REQUIRED) +- if (OpenMP_FOUND) +- set(CCOMMON_OPT "${CCOMMON_OPT} ${OpenMP_C_FLAGS} -DUSE_OPENMP") +- set(FCOMMON_OPT "${FCOMMON_OPT} ${OpenMP_Fortran_FLAGS}") ++ if(OpenMP_C_FOUND) ++ set(CCOMMON_OPT "${CCOMMON_OPT} -DUSE_OPENMP") ++ link_libraries($<$:OpenMP::OpenMP_C>) ++ if(OpenMP_C_INCLUDE_DIRS) ++ include_directories("${OpenMP_C_INCLUDE_DIRS}") ++ endif() ++ else() ++ message(FATAL_ERROR "OpenMP C component not found") ++ endif() ++ if(OpenMP_Fortran_FOUND) ++ link_libraries($<$:OpenMP::OpenMP_Fortran>) ++ if(OpenMP_Fortran_INCLUDE_DIRS) ++ include_directories("${OpenMP_Fortran_INCLUDE_DIRS}") ++ endif() ++ elseif(NOT NOFORTRAN) ++ message(FATAL_ERROR "OpenMP Fortran component not found") + endif() + endif () + diff --git a/recipes/openblas/all/test_package/CMakeLists.txt b/recipes/openblas/all/test_package/CMakeLists.txt index eaf07820c3b1c..63d3ce271287f 100644 --- a/recipes/openblas/all/test_package/CMakeLists.txt +++ b/recipes/openblas/all/test_package/CMakeLists.txt @@ -1,7 +1,11 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.15) project(test_package) -find_package(OpenBLAS REQUIRED CONFIG) +find_package(OpenBLAS CONFIG) +find_package(OpenBLAS64 CONFIG) +if(NOT OpenBLAS_FOUND AND NOT OpenBLAS64_FOUND) + message(FATAL_ERROR "OpenBLAS not found") +endif() add_executable(${PROJECT_NAME} test_package.cpp) target_link_libraries(${PROJECT_NAME} OpenBLAS::OpenBLAS) diff --git a/recipes/openblas/all/test_package/conanfile.py b/recipes/openblas/all/test_package/conanfile.py index 02eb5ce439fb4..f789a1a96acfa 100644 --- a/recipes/openblas/all/test_package/conanfile.py +++ b/recipes/openblas/all/test_package/conanfile.py @@ -4,11 +4,9 @@ import os -# It will become the standard on Conan 2.x class TestPackageConan(ConanFile): settings = "os", "arch", "compiler", "build_type" - generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv" - test_type = "explicit" + generators = "CMakeDeps", "CMakeToolchain" def requirements(self): self.requires(self.tested_reference_str) diff --git a/recipes/openblas/config.yml b/recipes/openblas/config.yml index e0f3014a9fdda..d2d673576ac73 100644 --- a/recipes/openblas/config.yml +++ b/recipes/openblas/config.yml @@ -1,4 +1,6 @@ versions: + "0.3.28": + folder: all "0.3.27": folder: all "0.3.26": @@ -7,15 +9,3 @@ versions: folder: all "0.3.24": folder: all - "0.3.20": - folder: all - "0.3.17": - folder: all - "0.3.15": - folder: all - "0.3.13": - folder: all - "0.3.12": - folder: all - "0.3.10": - folder: all