bazelbuild / rules_foreign_cc

Build rules for interfacing with "foreign" (non-Bazel) build systems (CMake, configure-make, GNU Make, boost, ninja, Meson)
https://bazelbuild.github.io/rules_foreign_cc
Apache License 2.0
650 stars 232 forks source link

Copy the CMake export files if they exist in the install folder. [Feature Request] #1195

Closed hhassoubi closed 3 weeks ago

hhassoubi commented 2 months ago

In modern CMake, an export file(s) are created during the build. Dependent external targets use these files to find and configure the package by name. The location of the exported files varies by project. The common location is

(lib*|share)/<package-name>/*.cmake
lib/<arch>/<package-name>/*.cmake
(lib*|share)/cmake/<package-name>/*.cmake
lib/<arch>/cmake/<package-name>/*.cmake
(lib*|share)/<package-name>/(cmake|CMake)/*.cmake
lib/<arch>/<package-name>/(cmake|CMake)/*.cmake

It is preferred to normalize the output location and use share/<package-name>/ (this is used in vcpkg). The package-name could be passed as an argument or use the rule name if empty.

If approved I could write it and send a PR.

jsharpe commented 2 months ago

The cmake rules already set CMAKE_PREFIX_PATH for any CMake based dependencies; its quite possible that this logic is not quite picking the correct path though so if that's what you're seeing I'd suggest that the correct path should be added to a provider that is generated by the CMake build rules that can then be consumed by downstream CMake rules.

hhassoubi commented 2 months ago

That will work for lagacy CMake project. Here is an example of Modern CMake

# lib A using cmake
# ...
add_library(A, ...)
install(TARGETS A EXPORT "A-targets" ...)
install(EXPORT ${targets_export_name} DESTINATION "share/A/A-config.cmake"  ) 
# ...

# lib B using cmake and depends on A
# ... 
find_package(A) # this step will look for "A-config.cmake" and will fail if not found
add_executable(B, ...)
target_link_libraries(B A ...)

for example here is the install output of a cmake project "fmt" https://github.com/fmtlib/fmt

cmake --install . --prefix=/tmp/z
-- Install configuration: "Release"
-- Installing: /tmp/z/lib/libfmt.so.9.1.0
-- Installing: /tmp/z/lib/libfmt.so.9
-- Installing: /tmp/z/lib/libfmt.so
-- Installing: /tmp/z/include/fmt/args.h
-- Installing: /tmp/z/include/fmt/chrono.h
-- Installing: /tmp/z/include/fmt/color.h
-- Installing: /tmp/z/include/fmt/compile.h
-- Installing: /tmp/z/include/fmt/core.h
-- Installing: /tmp/z/include/fmt/format.h
-- Installing: /tmp/z/include/fmt/format-inl.h
-- Installing: /tmp/z/include/fmt/os.h
-- Installing: /tmp/z/include/fmt/ostream.h
-- Installing: /tmp/z/include/fmt/printf.h
-- Installing: /tmp/z/include/fmt/ranges.h
-- Installing: /tmp/z/include/fmt/std.h
-- Installing: /tmp/z/include/fmt/xchar.h
-- Installing: /tmp/z/lib/cmake/fmt/fmt-config.cmake
-- Installing: /tmp/z/lib/cmake/fmt/fmt-config-version.cmake
-- Installing: /tmp/z/lib/cmake/fmt/fmt-targets.cmake
-- Installing: /tmp/z/lib/cmake/fmt/fmt-targets-release.cmake
-- Installing: /tmp/z/lib/pkgconfig/fmt.pc
jsharpe commented 2 months ago

Could you put together a simple example in the examples folder that shows how it breaks with the modern cmake style? We can then work out what we need to pass in a provider to correctly allow downstream invocations of CMake to find the right config files.

hhassoubi commented 2 months ago

Hello, I realized that the issue is related to my code. I was using a different target name for the Bazel target than what CMake is expecting for a package name. In the example below, building @spdlog//:spdlog will fail unless you rename the target bzl-fmt to fmt. The reason is that the .ext_build_deps uses the name of the Bazel target as a prefix to the install dir Thank you for your time. Please close the issue at your convenience.



http_archive(
    name = "fmt",
    sha256 = "312151a2d13c8327f5c9c586ac6cf7cddc1658e8f53edae0ec56509c8fa516c9",
    strip_prefix = "fmt-10.2.1",
    url = "https://github.com/fmtlib/fmt/releases/download/10.2.1/fmt-10.2.1.zip",
    build_file_content = """
load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
filegroup(
    name = "fmt_all",
    srcs = glob(["**"]),
)
cmake (
    name = "bzl-fmt",
    lib_source = ":fmt_all",
    out_static_libs = ["libfmt.a"],
    cache_entries = {
        "FMT_TEST":"OFF",
        "FMT_DOC":"OFF"
    },
    visibility = ["//visibility:public"]
)
    """
)

http_archive(
    name = "spdlog",
    sha256 = "429a6b73ade8285cb21f83bacf89e2821dd1720ea7faa3cb518ffe04b4e00efc",
    strip_prefix = "spdlog-1.14.0",
    url = "https://github.com/gabime/spdlog/archive/refs/tags/v1.14.0.tar.gz",
    build_file_content = """
load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
filegroup(
    name = "spdlog_all",
    srcs = glob(["**"]),
)
cmake (
    name = "spdlog",
    generate_args = ["--debug-find"],
    lib_source = ":spdlog_all",
    out_static_libs = ["libspdlog.a"],
    cache_entries = {
        "SPDLOG_FMT_EXTERNAL": "ON",
        "SPDLOG_INSTALL": "ON",
        "SPDLOG_BUILD_EXAMPLE": "OFF"
    },
    deps = ["@fmt//:bzl-fmt"],
    visibility = ["//visibility:public"]
)
    """
)```
jsharpe commented 2 months ago

Ok, great to know.

BTW in case you weren't aware there are native build files available in the BCR for fmt and spdlog: https://registry.bazel.build/modules/spdlog and https://registry.bazel.build/modules/fmt

I'd always recommend using native build files whenever possible as bazel will be more efficient this way.