Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Would it make sense to have a rule that exports a cc_library() into a UNIX-style lib/include directory? #1206

Open
EdSchouten opened this issue Jun 3, 2024 · 1 comment

Comments

@EdSchouten
Copy link

EdSchouten commented Jun 3, 2024

Hey! 👋

Last week I was working on setting up rules_rust's crate_repository() in a project of mine. One of the crates, openssl-sys, has a dependency on OpenSSL. In my case my project already had a copy of OpenSSL laying around, which I'm cc_import()ing. I wanted to reuse that one for the Rust crate, but ran into a slight issue: I can set build_script_env = {"OPENSSL_DIR": ...}, but that expects the library to be placed in a UNIX-style directory layout. So header files in include/, and the libraries themselves in lib/. I ended up solving this by writing a rule like this:

load("@bazel_skylib//lib:paths.bzl", "paths")

def _gather_library_impl(ctx):
    cc_info = ctx.attr.lib[CcInfo]
    files = []

    # Copy over all header files.
    for header in cc_info.compilation_context.headers.to_list():
        for include in cc_info.compilation_context.includes.to_list():
            if header.path.startswith(include + "/"):
                header_symlink = ctx.actions.declare_file(paths.join(ctx.attr.name, "include", paths.relativize(header.path, include)))
                ctx.actions.symlink(
                    output = header_symlink,
                    target_file = header,
                )
                files.append(header_symlink)

    # Copy over all shared libraryes.
    for linker_input in cc_info.linking_context.linker_inputs.to_list():
        for library in linker_input.libraries:
            # Remove version number from shared object filenames, as
            # most compilers will only search for files whose names end
            # with ".so".
            name = paths.basename(library.dynamic_library.path)
            if ".so." in name:
                base_name = name.split(".so.")[0] + ".so"
                library_symlink = ctx.actions.declare_file(paths.join(ctx.attr.name, "lib", base_name))
                ctx.actions.symlink(
                    output = library_symlink,
                    target_file = library.dynamic_library,
                )
                files.append(library_symlink)

            library_symlink = ctx.actions.declare_file(paths.join(ctx.attr.name, "lib", name))
            ctx.actions.symlink(
                output = library_symlink,
                target_file = library.dynamic_library,
            )
            files.append(library_symlink)

    return [DefaultInfo(files = depset(files))]

# Rule for turning a cc_library() into a UNIX-style directory layout.
# The "include" subdirectory contains all header files, while the "lib"
# subdirectory contains all shared objects.
gather_library = rule(
    implementation = _gather_library_impl,
    attrs = {
        "lib": attr.label(mandatory = True),
    },
)

Pretty slick, because now I can just write:

cc_import(
     name = "openssl",
     ...,
)

gather_library(
    name = "openssl_gathered",
    lib = "openssl",
)

Now I just need to turn it into a TreeArtifact:

copy_to_directory(
     name = "openssl_directory",
     srcs = ["openssl_gathered"],
     root_paths = ["openssl_gathered"],
)

And now I can build the openssl-sys crate by placing this in WORKSPACE:

crates_repository(
    name = "crates",
    annotations = {
        "openssl-sys": [crate.annotation(
            build_script_data = ["@openssl//:openssl_directory"],
            build_script_env = {
                "OPENSSL_DIR": "$(execpath @openssl//:openssl_directory)",
                "OPENSSL_LIBS": "",
            },
            deps = ["@openssl//:openssl"],
        )],
    },
)

Now I'm perfectly happy to hold on to that gather_directory() rule of mine. But at the same I was wondering whether it would make sense to add something like this to rules_foreign_cc as a utility function. Seems like a useful addition. Just let me know, and I'll send out a PR.

@jsharpe
Copy link
Member

jsharpe commented Jun 3, 2024

Yes, this seems pretty useful; it can also be useful for e.g. building zlib natively with bazel and then putting it into a unix file structure for consumption by a configure_make project or similar.
I wonder what a good test for this would be to include - maybe zlib (as included in the CI images: https://github.com/bazelbuild/continuous-integration/blob/9d79fb4ed17c28ca989a5bedef0ea97dbec031ea/buildkite/docker/ubuntu2204/Dockerfile#L52) is a good enough simple candidate for inclusion here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants