Search code examples
c++makefilecompilationbazelzlib

Bazel C++ how to include a library that needs to be cloned from github then build before it can be linked?


The library I need to use is: https://github.com/madler/zlib .

To install it separately, I can do:

git clone https://github.com/madler/zlib
cd zlib
make 

Then I can use it during compilation by including "-lz" flag, such as:

g++ -o main main.cpp std=c++17 -lz

I want to make "install zlib and build" a part of my bazel build, instead of asking a user to clone and build zlib manually.

My Bazel build file is as follow:

load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    copts = ["-std=c++17","-O3"],
    linkopts = ["-lz"]
)

What should I add so that when I run "bazel build ...", zlib is also cloned and installed (make)?

Edit: hello-world.cc program to test compilation:

#include <ctime>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <optional>
#include <vector>
#include <zlib.h>

class GzReader {
 private:
  static constexpr int BUFFER_SIZE = 1 << 20;
  const std::string filename_;
  gzFile file_;
  char *buffer_;
  int line_size_, idx_, last_idx_;                                          
  bool finished_;
  size_t line_count_;

 public:
  GzReader() = delete;

  explicit GzReader(std::string filename) : filename_(filename) {
    file_ = gzopen(filename_.c_str(), "r");
    if (!file_) {
      throw std::runtime_error("Could not open file");
    }

    buffer_ = new char[BUFFER_SIZE + 1];
    line_count_ = 0;
    idx_ = 0;
    last_idx_ = 0;
    line_size_ = gzread(file_, buffer_, BUFFER_SIZE);
    buffer_[line_size_] = '\0';
    finished_ = (line_size_ == 0);
  }

  ~GzReader() { 
    delete[] buffer_; 
    gzclose(file_); 
  }
};

static const char *file_name = "text.gz";
int main() {
  GzReader reader(file_name);  
  return 0;
}

Solution

  • Create a zlib.BUILD with the following content:

    # Copied from https://github.com/protocolbuffers/protobuf/blob/master/third_party/zlib.BUILD
    
    #  Copyright 2008 Google Inc.  All rights reserved.
    #
    #  Redistribution and use in source and binary forms, with or without
    #  modification, are permitted provided that the following conditions are
    #  met:
    #
    #      * Redistributions of source code must retain the above copyright
    #  notice, this list of conditions and the following disclaimer.
    #      * Redistributions in binary form must reproduce the above
    #  copyright notice, this list of conditions and the following disclaimer
    #  in the documentation and/or other materials provided with the
    #  distribution.
    #      * Neither the name of Google Inc. nor the names of its
    #  contributors may be used to endorse or promote products derived from
    #  this software without specific prior written permission.
    #
    #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    #  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    #  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    #  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    #  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    #  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    #  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    #  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    #  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    #  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    #
    #  Code generated by the Protocol Buffer compiler is owned by the owner
    #  of the input file used when generating it.  This code is not
    #  standalone and requires a support library to be linked with it.  This
    #  support library is itself covered by the above license.
    
    load("@rules_cc//cc:defs.bzl", "cc_library")
    
    licenses(["notice"])  # BSD/MIT-like license (for zlib)
    
    _ZLIB_HEADERS = [
        "crc32.h",
        "deflate.h",
        "gzguts.h",
        "inffast.h",
        "inffixed.h",
        "inflate.h",
        "inftrees.h",
        "trees.h",
        "zconf.h",
        "zlib.h",
        "zutil.h",
    ]
    
    _ZLIB_PREFIXED_HEADERS = ["zlib/include/" + hdr for hdr in _ZLIB_HEADERS]
    
    # In order to limit the damage from the `includes` propagation
    # via `:zlib`, copy the public headers to a subdirectory and
    # expose those.
    genrule(
        name = "copy_public_headers",
        srcs = _ZLIB_HEADERS,
        outs = _ZLIB_PREFIXED_HEADERS,
        cmd = "cp $(SRCS) $(@D)/zlib/include/",
    )
    
    cc_library(
        name = "zlib",
        srcs = [
            "adler32.c",
            "compress.c",
            "crc32.c",
            "deflate.c",
            "gzclose.c",
            "gzlib.c",
            "gzread.c",
            "gzwrite.c",
            "infback.c",
            "inffast.c",
            "inflate.c",
            "inftrees.c",
            "trees.c",
            "uncompr.c",
            "zutil.c",
            # Include the un-prefixed headers in srcs to work
            # around the fact that zlib isn't consistent in its
            # choice of <> or "" delimiter when including itself.
        ] + _ZLIB_HEADERS,
        hdrs = _ZLIB_PREFIXED_HEADERS,
        copts = select({
            "@bazel_tools//src/conditions:windows": [],
            "//conditions:default": [
                "-Wno-unused-variable",
                "-Wno-implicit-function-declaration",
            ],
        }),
        includes = ["zlib/include/"],
        visibility = ["//visibility:public"],
    )
    

    Add to your WORKSPACE:

    load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
    load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
    
    maybe(
        http_archive,
        name = "net_zlib_zlib",
        build_file = "@openexr//:bazel/third_party/zlib.BUILD",
        sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1",
        strip_prefix = "zlib-1.2.11",
        urls = [
            "https://zlib.net/zlib-1.2.11.tar.gz",
            "https://mirror.bazel.build/zlib.net/zlib-1.2.11.tar.gz",
        ],
    )
    

    Add to the deps attribute of your cc_binary:

    deps = [
        "@net_zlib_zlib//:zlib",
    ],
    

    Example: OpenEXR depends on zlib - see here