diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e71c46671..b06184dc01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,6 +172,10 @@ CMAKE_DEPENDENT_OPTION(BUILD_ZLIB "Build the ZLIB dependency Library" OFF "NOT BUILD_DEPS" ON) message(STATUS "Build ZLIB: ${BUILD_ZLIB}") +CMAKE_DEPENDENT_OPTION(BUILD_BZip2 "Build the BZip2 dependency Library" OFF + "NOT BUILD_DEPS" ON) +message(STATUS "Build BZip2: ${BUILD_BZip2}") + CMAKE_DEPENDENT_OPTION(BUILD_absl "Build the abseil-cpp dependency Library" OFF "NOT BUILD_DEPS" ON) message(STATUS "Build abseil-cpp: ${BUILD_absl}") diff --git a/cmake/README.md b/cmake/README.md index 039a51143c..157e9cbfeb 100644 --- a/cmake/README.md +++ b/cmake/README.md @@ -114,6 +114,7 @@ the option `-DBUILD_DEPS=ON` (`OFF` by default) or compile some of them using the options below (see [CMake Options](#cmake-options) below). * zlib (`BUILD_ZLIB`), +* bzip2 (`BUILD_BZip2`), * Google Abseil-cpp (`BUILD_absl`), * Google Protobuf (`BUILD_Protobuf`), * COIN-OR solvers: diff --git a/cmake/check_deps.cmake b/cmake/check_deps.cmake index 4e6cc73058..b7d5b2c018 100644 --- a/cmake/check_deps.cmake +++ b/cmake/check_deps.cmake @@ -16,6 +16,10 @@ if(NOT TARGET ZLIB::ZLIB) message(FATAL_ERROR "Target ZLIB::ZLIB not available.") endif() +if(NOT TARGET BZip2::BZip2) + message(FATAL_ERROR "Target BZip2::BZip2 not available.") +endif() + if(NOT TARGET absl::base) message(FATAL_ERROR "Target absl::base not available.") endif() diff --git a/cmake/cpp.cmake b/cmake/cpp.cmake index 24b9119de3..9cdc27ec1e 100644 --- a/cmake/cpp.cmake +++ b/cmake/cpp.cmake @@ -555,6 +555,7 @@ endif() target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS} ZLIB::ZLIB + BZip2::BZip2 ${ABSL_DEPS} protobuf::libprotobuf ${RE2_DEPS} diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt index 642ced627a..7d2ea4b967 100644 --- a/cmake/dependencies/CMakeLists.txt +++ b/cmake/dependencies/CMakeLists.txt @@ -73,6 +73,28 @@ if(BUILD_ZLIB) message(CHECK_PASS "fetched") endif() +# ############################################################################## +# BZip2 +# ############################################################################## +if(BUILD_BZip2) + message(CHECK_START "Fetching BZip2") + list(APPEND CMAKE_MESSAGE_INDENT " ") + set(ENABLE_LIB_ONLY ON) + set(ENABLE_TESTS OFF) + FetchContent_Declare( + BZip2 + GIT_REPOSITORY "https://gitlab.com/bzip2/bzip2.git" + GIT_TAG "master" + # GIT_TAG "bzip2-1.0.8" # CMake support not available + GIT_SHALLOW TRUE + PATCH_COMMAND git apply --ignore-whitespace + "${CMAKE_CURRENT_LIST_DIR}/../../patches/bzip2.patch" + ) + FetchContent_MakeAvailable(BZip2) + list(POP_BACK CMAKE_MESSAGE_INDENT) + message(CHECK_PASS "fetched") +endif() + # ############################################################################## # ABSEIL-CPP # ############################################################################## diff --git a/cmake/docs/cmake.dot b/cmake/docs/cmake.dot index f16a364e8c..30a7947e2a 100644 --- a/cmake/docs/cmake.dot +++ b/cmake/docs/cmake.dot @@ -34,6 +34,13 @@ digraph CMake { label = "madler/zlib.git + cmake patch"; } + subgraph clusterBZIP2 { + BZip2 [label="BZip2::BZip2", color=royalblue]; + + color=royalblue; + label = "bzip2/bzip2.git + cmake patch"; + } + subgraph clusterAbsl { Absl [label="absl::absl_*", color=royalblue]; @@ -195,6 +202,7 @@ digraph CMake { PKG_CPP -> EX_CPP; ZLIB -> OR_SRC; + BZip2 -> OR_SRC; Absl -> OR_SRC; Protobuf -> OR_SRC; Re2 -> OR_SRC; diff --git a/cmake/docs/deps.dot b/cmake/docs/deps.dot index 0ddff8381b..3cf09233dd 100644 --- a/cmake/docs/deps.dot +++ b/cmake/docs/deps.dot @@ -11,6 +11,13 @@ digraph CMakeDeps { label = "madler/zlib.git + cmake patch"; } + subgraph clusterBZip2 { + BZip2 [label="BZip2::BZip2"]; + + color=royalblue; + label = "bzip2/bzip2.git + cmake patch"; + } + subgraph clusterAbsl { Absl [label="absl::absl_*"]; diff --git a/cmake/dotnet.cmake b/cmake/dotnet.cmake index 83e00ba151..9366fce102 100644 --- a/cmake/dotnet.cmake +++ b/cmake/dotnet.cmake @@ -377,6 +377,9 @@ set(is_not_windows "$>") set(need_unix_zlib_lib "$>") set(need_windows_zlib_lib "$>") +set(need_unix_bzip2_lib "$>") +set(need_windows_bzip2_lib "$>") + set(need_unix_absl_lib "$>") set(need_windows_absl_lib "$>") diff --git a/cmake/java.cmake b/cmake/java.cmake index b890416234..c7ecb449bd 100644 --- a/cmake/java.cmake +++ b/cmake/java.cmake @@ -280,6 +280,9 @@ set(is_not_windows "$>") set(need_unix_zlib_lib "$>") set(need_windows_zlib_lib "$>") +set(need_unix_bzip2_lib "$>") +set(need_windows_bzip2_lib "$>") + set(need_unix_absl_lib "$>") set(need_windows_absl_lib "$>") @@ -314,6 +317,11 @@ add_custom_command( $<${need_unix_zlib_lib}:$> $<${need_windows_zlib_lib}:$> ${JAVA_RESSOURCES_PATH}/${JAVA_NATIVE_PROJECT}/ + COMMAND ${CMAKE_COMMAND} -E + $,copy,true> + $<${need_unix_bzip2_lib}:$> + $<${need_windows_bzip2_lib}:$> + ${JAVA_RESSOURCES_PATH}/${JAVA_NATIVE_PROJECT}/ COMMAND ${CMAKE_COMMAND} -E $,copy,true> $<${need_unix_absl_lib}:$> diff --git a/cmake/ortoolsConfig.cmake.in b/cmake/ortoolsConfig.cmake.in index d497e54dd7..30d294cd1c 100644 --- a/cmake/ortoolsConfig.cmake.in +++ b/cmake/ortoolsConfig.cmake.in @@ -11,6 +11,10 @@ if(NOT TARGET ZLIB::ZLIB) find_dependency(ZLIB REQUIRED) endif() +if(NOT TARGET BZip2::BZip2) + find_dependency(BZip2 REQUIRED) +endif() + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.9.6") set(CONFIG_FLAG CONFIG) endif() diff --git a/cmake/python.cmake b/cmake/python.cmake index f26dbd83e3..bd13152ec0 100644 --- a/cmake/python.cmake +++ b/cmake/python.cmake @@ -458,6 +458,9 @@ set(is_not_windows "$>") set(need_unix_zlib_lib "$>") set(need_windows_zlib_lib "$>") +set(need_unix_bzip2_lib "$>") +set(need_windows_bzip2_lib "$>") + set(need_unix_absl_lib "$>") set(need_windows_absl_lib "$>") @@ -492,6 +495,11 @@ add_custom_command( $<${need_unix_zlib_lib}:$> $<${need_windows_zlib_lib}:$> ${PYTHON_PROJECT}/.libs + COMMAND ${CMAKE_COMMAND} -E + $,copy,true> + $<${need_unix_bzip2_lib}:$> + $<${need_windows_bzip2_lib}:$> + ${PYTHON_PROJECT}/.libs COMMAND ${CMAKE_COMMAND} -E $,copy,true> $<${need_unix_absl_lib}:$> diff --git a/cmake/system_deps.cmake b/cmake/system_deps.cmake index db2d2567cc..8fcc19cdc7 100644 --- a/cmake/system_deps.cmake +++ b/cmake/system_deps.cmake @@ -26,6 +26,10 @@ if(NOT BUILD_ZLIB AND NOT TARGET ZLIB::ZLIB) find_package(ZLIB REQUIRED) endif() +if(NOT BUILD_BZip2 AND NOT TARGET BZip2::BZip2) + find_package(BZip2 REQUIRED) +endif() + if(NOT BUILD_absl AND NOT TARGET absl::base) find_package(absl REQUIRED) endif() diff --git a/ortools/base/BUILD.bazel b/ortools/base/BUILD.bazel index 762581c7c9..cf0b7756fe 100644 --- a/ortools/base/BUILD.bazel +++ b/ortools/base/BUILD.bazel @@ -246,7 +246,6 @@ cc_library( "helpers.h", "options.h", ], - defines = ["USE_BZIP2"], deps = [ ":status_macros", "@abseil-cpp//absl/log", diff --git a/ortools/base/CMakeLists.txt b/ortools/base/CMakeLists.txt index c05028a50e..ffddb85b86 100644 --- a/ortools/base/CMakeLists.txt +++ b/ortools/base/CMakeLists.txt @@ -35,6 +35,7 @@ target_include_directories(${NAME} PRIVATE ${PROJECT_BINARY_DIR}) target_link_libraries(${NAME} PRIVATE ZLIB::ZLIB + BZip2::BZip2 absl::base absl::strings absl::str_format diff --git a/ortools/base/file.cc b/ortools/base/file.cc index 17154efcaf..2759af2524 100644 --- a/ortools/base/file.cc +++ b/ortools/base/file.cc @@ -16,12 +16,9 @@ #include #include -#if defined(USE_BZIP2) -#include -#endif -#include - +#include #include + #if defined(_MSC_VER) #include #define access _access @@ -33,7 +30,6 @@ #include #include #include -#include // NOLINT #include #include @@ -42,16 +38,14 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "bzlib.h" #include "google/protobuf/io/tokenizer.h" #include "google/protobuf/message.h" #include "google/protobuf/text_format.h" +#include "zlib.h" namespace { -enum class Format { - NORMAL_FILE, - GZIP_FILE, - BZIP2_FILE -}; +enum class Format { NORMAL_FILE, GZIP_FILE, BZIP2_FILE }; static Format GetFormatFromName(absl::string_view name) { const int size = name.size(); @@ -67,7 +61,7 @@ static Format GetFormatFromName(absl::string_view name) { class CFile : public File { public: CFile(FILE* c_file, absl::string_view name) : File(name), f_(c_file) {} - virtual ~CFile() = default; + ~CFile() override = default; // Reads "size" bytes to buf from file, buf should be pre-allocated. size_t Read(void* buf, size_t size) override { @@ -80,7 +74,7 @@ class CFile : public File { } // Closes the file and delete the underlying FILE* descriptor. - absl::Status Close(int flags) override { + absl::Status Close(int /*flags*/) override { absl::Status status; if (f_ == nullptr) { return status; @@ -113,50 +107,48 @@ class CFile : public File { }; class GzFile : public File { - public: + public: GzFile(gzFile gz_file, absl::string_view name) : File(name), f_(gz_file) {} - virtual ~GzFile() = default; + ~GzFile() override = default; - // Reads "size" bytes to buf from file, buf should be pre-allocated. - size_t Read(void* buf, size_t size) override { - return gzread(f_, buf, size); - } + // Reads "size" bytes to buf from file, buf should be pre-allocated. + size_t Read(void* buf, size_t size) override { return gzread(f_, buf, size); } - // Writes "size" bytes of buf to file, buf should be pre-allocated. - size_t Write(const void* buf, size_t size) override { - return gzwrite(f_, buf, size); - } + // Writes "size" bytes of buf to file, buf should be pre-allocated. + size_t Write(const void* buf, size_t size) override { + return gzwrite(f_, buf, size); + } - // Closes the file and delete the underlying FILE* descriptor. - absl::Status Close(int flags) override { - absl::Status status; - if (f_ == nullptr) { - return status; - } - if (gzclose(f_) == 0) { - f_ = nullptr; - } else { - status.Update( - absl::Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("Could not close file '", name_, "'"))); - } - delete this; - return status; - } + // Closes the file and delete the underlying FILE* descriptor. + absl::Status Close(int /*flags*/) override { + absl::Status status; + if (f_ == nullptr) { + return status; + } + if (gzclose(f_) == 0) { + f_ = nullptr; + } else { + status.Update( + absl::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("Could not close file '", name_, "'"))); + } + delete this; + return status; + } - // Flushes buffer. - bool Flush() override { return gzflush(f_, Z_FINISH) == Z_OK; } + // Flushes buffer. + bool Flush() override { return gzflush(f_, Z_FINISH) == Z_OK; } - // Returns file size. - size_t Size() override { + // Returns file size. + size_t Size() override { gzFile file; std::string null_terminated_name = std::string(name_); - #if defined(_MSC_VER) - file = gzopen (null_terminated_name.c_str(), "rb"); - #else - file = gzopen (null_terminated_name.c_str(), "r"); - #endif - if (! file) { +#if defined(_MSC_VER) + file = gzopen(null_terminated_name.c_str(), "rb"); +#else + file = gzopen(null_terminated_name.c_str(), "r"); +#endif + if (!file) { LOG(FATAL) << "Cannot get the size of '" << name_ << "': " << strerror(errno); } @@ -164,7 +156,7 @@ class GzFile : public File { const int kLength = 5 * 1024; unsigned char buffer[kLength]; size_t uncompressed_size = 0; - while (1) { + while (true) { int err; int bytes_read; bytes_read = gzread(file, buffer, kLength - 1); @@ -183,54 +175,53 @@ class GzFile : public File { } gzclose(file); return uncompressed_size; - } + } - bool Open() const override { return f_ != nullptr; } + bool Open() const override { return f_ != nullptr; } - private: - gzFile f_; - }; + private: + gzFile f_; +}; - #if defined(USE_BZIP2) - class Bz2File : public File { - public: +class Bz2File : public File { + public: Bz2File(BZFILE* bz_file, absl::string_view name) : File(name), f_(bz_file) {} - virtual ~Bz2File() = default; + ~Bz2File() override = default; - // Reads "size" bytes to buf from file, buf should be pre-allocated. - size_t Read(void* buf, size_t size) override { - return BZ2_bzread(f_, buf, size); - } + // Reads "size" bytes to buf from file, buf should be pre-allocated. + size_t Read(void* buf, size_t size) override { + return BZ2_bzread(f_, buf, size); + } - // Writes "size" bytes of buf to file, buf should be pre-allocated. - size_t Write(const void* buf, size_t size) override { - return BZ2_bzwrite(f_, const_cast(buf), size); - } + // Writes "size" bytes of buf to file, buf should be pre-allocated. + size_t Write(const void* buf, size_t size) override { + return BZ2_bzwrite(f_, const_cast(buf), size); + } - // Closes the file and delete the underlying FILE* descriptor. - absl::Status Close(int flags) override { - absl::Status status; - if (f_ == nullptr) { - return absl::OkStatus(); - } - BZ2_bzclose(f_); - f_ = nullptr; - delete this; - return absl::OkStatus(); - } + // Closes the file and delete the underlying FILE* descriptor. + absl::Status Close(int /*flags*/) override { + absl::Status status; + if (f_ == nullptr) { + return absl::OkStatus(); + } + BZ2_bzclose(f_); + f_ = nullptr; + delete this; + return absl::OkStatus(); + } - // Flushes buffer. - bool Flush() override { return BZ2_bzflush(f_) == 0; } + // Flushes buffer. + bool Flush() override { return BZ2_bzflush(f_) == 0; } - // Returns file size. - size_t Size() override { + // Returns file size. + size_t Size() override { BZFILE* file; std::string null_terminated_name = std::string(name_); - #if defined(_MSC_VER) - file = BZ2_bzopen (null_terminated_name.c_str(), "rb"); - #else - file = BZ2_bzopen (null_terminated_name.c_str(), "r"); - #endif +#if defined(_MSC_VER) + file = BZ2_bzopen(null_terminated_name.c_str(), "rb"); +#else + file = BZ2_bzopen(null_terminated_name.c_str(), "r"); +#endif if (!file) { LOG(FATAL) << "Cannot get the size of '" << name_ << "': " << strerror(errno); @@ -239,8 +230,7 @@ class GzFile : public File { const int kLength = 5 * 1024; unsigned char buffer[kLength]; size_t uncompressed_size = 0; - while (1) { - int err; + while (true) { int bytes_read; bytes_read = BZ2_bzread(file, buffer, kLength - 1); uncompressed_size += bytes_read; @@ -248,14 +238,13 @@ class GzFile : public File { } BZ2_bzclose(file); return uncompressed_size; - } + } - bool Open() const override { return f_ != nullptr; } + bool Open() const override { return f_ != nullptr; } - private: - BZFILE* f_; - }; - #endif // USE_BZIP2 + private: + BZFILE* f_; +}; } // namespace @@ -292,17 +281,14 @@ File* File::Open(absl::string_view file_name, absl::string_view mode) { return new GzFile(gz_file, file_name); } case Format::BZIP2_FILE: { -#if defined(USE_BZIP2) - BZFILE* bz_file = - BZ2_bzopen(null_terminated_name.c_str(), null_terminated_mode.c_str()); + BZFILE* bz_file = BZ2_bzopen(null_terminated_name.c_str(), + null_terminated_mode.c_str()); if (!bz_file) return nullptr; return new Bz2File(bz_file, file_name); -#else - LOG(ERROR) << "Using bzip2 files is not supported"; - return nullptr; -#endif } } + // never reach + return nullptr; } int64_t File::ReadToString(std::string* line, uint64_t max_length) { @@ -399,8 +385,8 @@ absl::Status WriteString(File* file, absl::string_view contents, absl::StrCat("Could not write ", contents.size(), " bytes")); } -absl::Status SetContents(absl::string_view file_name, absl::string_view contents, - Options options) { +absl::Status SetContents(absl::string_view file_name, + absl::string_view contents, Options options) { File* file; // For windows, the "b" is added in file::Open. auto status = file::Open(file_name, "w", &file, options); @@ -430,12 +416,12 @@ absl::Status GetTextProto(absl::string_view file_name, absl::StrCat("Could not read proto from '", file_name, "'.")); } - // Attempt to decode ASCII before deciding binary. Do it in this order because - // it is much harder for a binary encoding to happen to be a valid ASCII - // encoding than the other way around. For instance "index: 1\n" is a valid - // (but nonsensical) binary encoding. We want to avoid printing errors for - // valid binary encodings if the ASCII parsing fails, and so specify a no-op - // error collector. + // Attempt to decode ASCII before deciding binary. Do it in this order + // because it is much harder for a binary encoding to happen to be a valid + // ASCII encoding than the other way around. For instance "index: 1\n" is a + // valid (but nonsensical) binary encoding. We want to avoid printing errors + // for valid binary encodings if the ASCII parsing fails, and so specify a + // no-op error collector. NoOpErrorCollector error_collector; google::protobuf::TextFormat::Parser parser; parser.RecordErrorsTo(&error_collector); diff --git a/ortools/dotnet/Google.OrTools.runtime.csproj.in b/ortools/dotnet/Google.OrTools.runtime.csproj.in index 69b2318a9a..62c8e1db01 100644 --- a/ortools/dotnet/Google.OrTools.runtime.csproj.in +++ b/ortools/dotnet/Google.OrTools.runtime.csproj.in @@ -29,6 +29,9 @@ $<@need_unix_zlib_lib@:;$> $<@need_windows_zlib_lib@:;$> + $<@need_unix_bzip2_lib@:;$> + $<@need_windows_bzip2_lib@:;$> + $<@need_unix_absl_lib@:;$> $<@need_unix_absl_lib@:;$> $<@need_unix_absl_lib@:;$> diff --git a/ortools/java/com/google/ortools/Loader.java b/ortools/java/com/google/ortools/Loader.java index d2cafcf889..5aa56c31a2 100644 --- a/ortools/java/com/google/ortools/Loader.java +++ b/ortools/java/com/google/ortools/Loader.java @@ -146,6 +146,7 @@ public class Loader { // libraries order does matter ! List> dlls = Arrays.asList((new AbstractMap.SimpleEntry("zlib1", true)), + (new AbstractMap.SimpleEntry("bz2", true)), (new AbstractMap.SimpleEntry("abseil_dll", true)), (new AbstractMap.SimpleEntry("re2", true)), (new AbstractMap.SimpleEntry("libutf8_validity", true)), diff --git a/ortools/python/__init__.py.in b/ortools/python/__init__.py.in index b71385b3ab..7a6500a38c 100644 --- a/ortools/python/__init__.py.in +++ b/ortools/python/__init__.py.in @@ -57,7 +57,7 @@ def _load_ortools_libs(): except: logger.error(f"ImportError: Cannot import WinDLL") else: - for dll in ["zlib1.dll", + for dll in ["zlib1.dll", "bz2.dll", "abseil_dll.dll", "utf8_validity.dll", "re2.dll", "libprotobuf.dll", "highs.dll", "libscip.dll", "ortools.dll"]: diff --git a/patches/bzip2.patch b/patches/bzip2.patch new file mode 100644 index 0000000000..ace4852290 --- /dev/null +++ b/patches/bzip2.patch @@ -0,0 +1,172 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c4b0b6e..30f7652 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,5 +1,10 @@ + cmake_minimum_required(VERSION 3.12) + ++# option() honors normal variables. ++if (POLICY CMP0077) ++ cmake_policy(SET CMP0077 NEW) ++endif() ++ + project(bzip2 + VERSION 1.1.0 + DESCRIPTION "This Bzip2/libbz2 a program and library for lossless block-sorting data compression." +@@ -283,8 +288,8 @@ set(BZ2_SOURCES + add_library(bz2_ObjLib OBJECT) + target_sources(bz2_ObjLib + PRIVATE ${BZ2_SOURCES} +- PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/bzlib_private.h +- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/bzlib.h) ++ bzlib_private.h ++ bzlib.h) + + # Windows resource file + set(BZ2_RES "") +@@ -299,21 +304,30 @@ endif() + + if(ENABLE_SHARED_LIB) + # The libbz2 shared library. +- add_library(bz2 SHARED ${BZ2_RES}) +- target_sources(bz2 +- PRIVATE ${BZ2_SOURCES} +- ${CMAKE_CURRENT_SOURCE_DIR}/libbz2.def +- PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/bzlib_private.h +- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/bzlib.h) ++ add_library(BZip2 SHARED ${BZ2_RES}) ++ target_sources(BZip2 ++ PRIVATE ${BZ2_SOURCES} ++ libbz2.def ++ bzlib_private.h ++ bzlib.h) ++ target_include_directories(BZip2 PUBLIC ++ $ ++ $ ++ ) + + # Always use '-fPIC'/'-fPIE' option for shared libraries. +- set_property(TARGET bz2 PROPERTY POSITION_INDEPENDENT_CODE ON) ++ set_property(TARGET BZip2 PROPERTY POSITION_INDEPENDENT_CODE ON) + +- set_target_properties(bz2 PROPERTIES ++ set_target_properties(BZip2 PROPERTIES + COMPILE_FLAGS "${WARNCFLAGS}" +- VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}) +- install(TARGETS bz2 DESTINATION ${CMAKE_INSTALL_LIBDIR}) ++ VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION} ++ OUTPUT_NAME bz2 ++ ) ++ install(TARGETS BZip2 ++ EXPORT ${PROJECT_NAME}Targets ++ DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(FILES bzlib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) ++ add_library(BZip2::BZip2 ALIAS BZip2) + + if(USE_OLD_SONAME) + # Hack to support the old libbz2.so.1.0 version by including an extra copy. +@@ -323,16 +337,22 @@ if(ENABLE_SHARED_LIB) + add_library(bz2_old_soname SHARED ${BZ2_RES}) + target_sources(bz2_old_soname + PRIVATE ${BZ2_SOURCES} +- ${CMAKE_CURRENT_SOURCE_DIR}/libbz2.def +- PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/bzlib_private.h +- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/bzlib.h ++ libbz2.def ++ bzlib_private.h ++ bzlib.h ++ ) ++ target_include_directories(bz2_old_soname PUBLIC ++ $ ++ $ + ) + set_target_properties(bz2_old_soname PROPERTIES + COMPILE_FLAGS "${WARNCFLAGS}" + VERSION ${LT_SOVERSION}.${LT_AGE} SOVERSION ${LT_SOVERSION}.${LT_AGE} + OUTPUT_NAME bz2 + ) +- install(TARGETS bz2_old_soname DESTINATION ${CMAKE_INSTALL_LIBDIR}) ++ install(TARGETS bz2_old_soname ++ EXPORT ${PROJECT_NAME}Targets ++ DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + endif() + endif() +@@ -341,9 +361,13 @@ if(ENABLE_STATIC_LIB) + # The libbz2 static library. + add_library(bz2_static STATIC) + target_sources(bz2_static +- PRIVATE ${BZ2_SOURCES} +- PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/bzlib_private.h +- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/bzlib.h) ++ PRIVATE ${BZ2_SOURCES} ++ bzlib_private.h ++ bzlib.h) ++ target_include_directories(bz2_static PUBLIC ++ $ ++ $ ++ ) + + # Use '-fPIC'/'-fPIE' option for static libraries by default. + # You may build with ENABLE_STATIC_LIB_IS_PIC=OFF to disable PIC for the static library. +@@ -357,8 +381,13 @@ if(ENABLE_STATIC_LIB) + SOVERSION ${LT_SOVERSION} + ARCHIVE_OUTPUT_NAME bz2_static) + target_compile_definitions(bz2_static PUBLIC BZ2_STATICLIB) +- install(TARGETS bz2_static DESTINATION ${CMAKE_INSTALL_LIBDIR}) ++ install(TARGETS bz2_static ++ EXPORT ${PROJECT_NAME}Targets ++ DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(FILES bzlib.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) ++ if(NOT TARGET BZip2) ++ add_library(BZip2::BZip2 ALIAS bz2_static) ++ endif() + endif() + + if(ENABLE_APP) +@@ -373,7 +402,9 @@ if(ENABLE_APP) + else() + target_compile_definitions(bzip2 PUBLIC BZ_LCCWIN32=0 BZ_UNIX) + endif() +- install(TARGETS bzip2 DESTINATION ${CMAKE_INSTALL_BINDIR}) ++ install(TARGETS bzip2 ++ EXPORT ${PROJECT_NAME}Targets ++ DESTINATION ${CMAKE_INSTALL_BINDIR}) + + # Create bzip2 copies bzcat and bunzip. + # The default behavior is altered in bzip2.c code by checking the program name. +@@ -391,7 +422,9 @@ if(ENABLE_APP) + else() + target_compile_definitions(bzip2recover PUBLIC BZ_LCCWIN32=0 BZ_UNIX) + endif() +- install(TARGETS bzip2recover DESTINATION ${CMAKE_INSTALL_BINDIR}) ++ install(TARGETS bzip2recover ++ EXPORT ${PROJECT_NAME}Targets ++ DESTINATION ${CMAKE_INSTALL_BINDIR}) + + if(ENABLE_EXAMPLES) + if(ENABLE_SHARED_LIB) +@@ -399,8 +432,10 @@ if(ENABLE_APP) + add_executable(dlltest) + target_sources(dlltest + PRIVATE dlltest.c) +- target_link_libraries(dlltest bz2) +- install(TARGETS dlltest DESTINATION ${CMAKE_INSTALL_BINDIR}) ++ target_link_libraries(dlltest BZip2) ++ install(TARGETS dlltest ++ EXPORT ${PROJECT_NAME}Targets ++ DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + endif() + +@@ -419,6 +454,10 @@ if(ENABLE_APP) + + endif() + ++install(EXPORT ${PROJECT_NAME}Targets ++ NAMESPACE BZip2:: ++ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) ++ + if(ENABLE_APP AND Python3_FOUND) + enable_testing() + add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})