Search code examples
bazelbazel-rules

Bazel Package: How to export build artifacts to another directory with external dependency?


I am new to Bazel. I have a project which is built with Bazel. It also used some third-party libraries which are also build from bazel (from source).

I am now trying to export my project as a standalone library to others. I can compile my project as a so and copy related hpp files to a directory.

But I don't know how to deal with the third party libraries. Is there a fancy way to do this? Or any examples I can refer to?

My goal is to :

  1. Comile the project as a so file. Copy to a specfic directory. (DONE)
  2. Copy all the header files with tree structure. (DONE).
  3. Copy all the external libraries into the specfic directory as well (NEED HELP)
  4. Copy the external libraries' header files into the same header directory (NEED HELP)

output:

  • include/

    • my_library_name/
    • third_party_name1/
    • third_party_name2/
  • library/

    • libmy_library.so
    • libthird_party_name1.so
    • libthird_party_name2.so

Solution

  • If you have control over the BUILD files of the third-party libraries, you could expose the header files separately and then use pkg_tar rules to collect the target files in the main project.

    For example, assuming a folder structure like the following:

    .
    |-- project
    |   |-- BUILD
    |   |-- mylib
    |   |   |-- BUILD
    |   |   |-- mylib.cpp
    |   |   `-- mylib.h
    |   `-- WORKSPACE
    `-- thirdparty
        |-- lib1
        |   |-- BUILD
        |   |-- lib1.cpp
        |   `-- lib1.h
        |-- lib2
        |   |-- BUILD
        |   |-- lib2.cpp
        |   `-- lib2.h
        `-- WORKSPACE
    

    your third-party libraries BUILD files could expose a filegroup and a cc_library:

    # thirdparty/lib1/BUILD
    
    filegroup(
        name = "headers",
        srcs = ["lib1.h"],
        visibility = ["//visibility:public"],
    )
    
    cc_library(
        name = "lib1",
        hdrs = [":headers"],
        srcs=["lib1.cpp"],
        visibility = ["//visibility:public"],
    )
    

    The same pattern will apply to thirdparty/lib2/BUILD and project/mylib/BUILD.

    Now in the main project you could have a main BUILD script that collects all the files into a tar archive:

    # project/BUILD
    
    load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
    
    pkg_tar(
        name = "release_headers",
        srcs = [
            "//mylib:headers",
            "@thirdparty//lib1:headers",
            "@thirdparty//lib2:headers",
        ],
        package_dir = "include",
    )
    
    pkg_tar(
        name = "release_libs",
        srcs = [
            "//mylib",
            "@thirdparty//lib1",
            "@thirdparty//lib2",
        ],
        package_dir = "lib",
    )
    
    pkg_tar(
        name = "release",
        deps = [
            ":release_headers",
            ":release_libs",
        ],
    )
    

    Building :release should create a tar file with the desired structure:

    $> bazel build :release
    ...
    Target //:release up-to-date:
      bazel-bin/release.tar
    ...
    
    $> tar -tf bazel-bin/release.tar
    ./
    ./include/
    ./include/mylib.h
    ./include/lib1.h
    ./include/lib2.h
    ./lib/
    ./lib/libmylib.a
    ./lib/libmylib.so
    ./lib/liblib1.a
    ./lib/liblib1.so
    ./lib/liblib2.a
    ./lib/liblib2.so
    

    You can also have the pkg_tar rules for the third-party libraries in the third-party workspace for better modularity.