From 2491891e7dc0e99129849b7ca83e4deccb94c662 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Tue, 30 Jan 2024 00:04:01 -0800 Subject: [PATCH] feat: query/print build information (#4124) New OIIO global attributes (read only): - "build:platform" retrieves the OS and CPU type that this build of OIIO is fof (e.g., "Linux/x86_64"). - "build:compiler" retrieves the compiler name and version used to build OIIO itself (e.g. "gcc 9.3"). - "build:dependencies" retrieves a semicolon-separated list of library dependencies -- both for image format support (like libtiff) but also general (boost, TBB, Python, fmt, OCIO). - "build:simd" is a new (preferred) synonym for the SIMD and other hardware capabilities selected at build time. The old name, "oiio:simd" is hereby softly deprecated as confusing. `oiiotool --buildinfo` is a new command that prints this information. Example output: ``` $ oiiotool --buildinfo OIIO 2.6.0.2spi | MacOS/x86_64 Build compiler: Apple clang 15.0 | C++17/201703 HW features enabled at build: sse2,sse3,ssse3,sse41,sse42 Dependencies: OpenEXR 3.2.1, LIBTIFF Version 4.6.0, jpeg-turbo 3.0.1/jp80, dcmtk 3.6.8, FFMpeg 6.0 (Lavf60.16.100), gif_lib 5.2.1, libheif 1.17.6, OpenJpeg 2.5.0, null 1.0, OpenVDB 11.0.0abi11, libpng 1.6.40, Ptex 2.4, libraw 0.21.2-Release, Webp 1.3.2, Boost 1.83, OpenColorIO 2.3.1, Python 3.11.7, TBB 2021.11.0, fmt 10.2.1 ``` This is all a convenience utility to make it easy to ask users to run a simple command that will tell us a lot of information relevant to resolving their issues in cases where it's related to some of their build-time compiler or dependencies. Also cleaned up the "bug report issue template" to clarify the instructions and specifically ask people to run this command and include the information with any bug report. --------- Signed-off-by: Larry Gritz --- .github/ISSUE_TEMPLATE/bug_report.md | 35 ++++++++----- CMakeLists.txt | 1 - src/cmake/checked_find_package.cmake | 12 ++++- src/cmake/externalpackages.cmake | 14 +++++- src/cmake/modules/FindWebP.cmake | 10 ++++ src/doc/oiiotool.rst | 5 ++ src/include/OpenImageIO/imageio.h | 23 +++++++-- src/libOpenImageIO/CMakeLists.txt | 16 ++++-- src/libOpenImageIO/imageio.cpp | 73 +++++++++++++++++++++++++++- src/libOpenImageIO/imageio_pvt.h.in | 3 ++ src/oiiotool/oiiotool.cpp | 65 ++++++++++++++++--------- src/openexr.imageio/exrinput.cpp | 2 - src/openexr.imageio/exrinput_c.cpp | 2 - src/python/CMakeLists.txt | 3 -- 14 files changed, 210 insertions(+), 54 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ef2dde4803..c1810d405f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,29 +8,40 @@ assignees: '' --- **Describe the bug** + A clear and concise description of what the bug is. What happened, and what did you expect to happen instead. + +**OpenImageIO version and dependencies** + +Please run `oiiotool --buildinfo` and paste the output here. + +Also please tell us if there was anything unusual about your environment or +nonstandard build options you used. + + **To Reproduce** + Steps to reproduce the behavior: 1. Do this... 2. Then this... 3. Then THIS happens (reproduce the exact error message if you can) +4. Whereas I expected this other specific thing to happen instead. + +If the problem occurs in your C++ or Python code that uses the OIIO APIs, can +you also reproduce the problem using oiiotool? If so, please describe the +exact command line that reproduces the problem. (Being able to reproduce the +problem using only OIIO components makes it a lot easier for the developers +investigate and makes it clear it's not your application's fault.) -**Expected behavior** -A clear and concise description of what you expected to happen. **Evidence** -Do you have error messages? (please quote exactly) Screenshots? Example -command lines or scripts that reliably reproduce the error? If it only -happens with certain image files, can you attach the smallest image you -can make that reproduces the problem? - -**Platform information:** - - OIIO branch/version: - - OS: - - C++ compiler: - - Any non-default build flags when you build OIIO: + +- Error messages (paste them here exactly) +- Screenshots (if helpful) +- Example input: If the problem only happens with certain image files, please + attach the smallest image you can make that reproduces the problem. **IF YOU ALREADY HAVE A CODE FIX:** There is no need to file a separate issue, diff --git a/CMakeLists.txt b/CMakeLists.txt index c3d4d20d1d..c7d48bcea4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,7 +197,6 @@ include_directories ( # Tell CMake to process the sub-directories add_subdirectory (src/libutil) -find_or_download_robin_map () # Add IO plugin directories -- if we are embedding plugins, we need to visit diff --git a/src/cmake/checked_find_package.cmake b/src/cmake/checked_find_package.cmake index 25315610e5..d707ff3a2f 100644 --- a/src/cmake/checked_find_package.cmake +++ b/src/cmake/checked_find_package.cmake @@ -9,6 +9,8 @@ set (OPTIONAL_DEPS "" CACHE STRING "Additional dependencies to consider optional (semicolon-separated list, or ALL)") option (ALWAYS_PREFER_CONFIG "Prefer a dependency's exported config file if it's available" OFF) +# Track all build deps we find with checked_find_package +set (CFP_ALL_BUILD_DEPS_FOUND "") # Utility function to list the names and values of all variables matching # the pattern (case-insensitive) @@ -66,6 +68,9 @@ endfunction () # file from the package before using a FindPackage.cmake module. # * Optional DEBUG turns on extra debugging information related to how # this package is found. +# * Found package "name version" or "name NONE" are accumulated in the list +# CFP_ALL_BUILD_DEPS_FOUND. If the optional NO_RECORD_NOTFOUND is +# supplied, un-found packags will not be recorded. # # N.B. This needs to be a macro, not a function, because the find modules # will set(blah val PARENT_SCOPE) and we need that to be the global scope, @@ -73,7 +78,7 @@ endfunction () macro (checked_find_package pkgname) cmake_parse_arguments(_pkg # prefix # noValueKeywords: - "REQUIRED;PREFER_CONFIG;DEBUG" + "REQUIRED;PREFER_CONFIG;DEBUG;NO_RECORD_NOTFOUND" # singleValueKeywords: "ENABLE;ISDEPOF;VERSION_MIN;VERSION_MAX;RECOMMEND_MIN;RECOMMEND_MIN_REASON" # multiValueKeywords: @@ -155,6 +160,8 @@ macro (checked_find_package pkgname) message (STATUS "${ColorYellow}Recommend ${pkgname} >= ${_pkg_RECOMMEND_MIN} ${_pkg_RECOMMEND_MIN_REASON} ${ColorReset}") endif () endif () + string (STRIP "${pkgname} ${${pkgname}_VERSION}" app_) + list (APPEND CFP_ALL_BUILD_DEPS_FOUND "${app_}") else () message (STATUS "${ColorRed}${pkgname} library not found ${ColorReset}") if (${pkgname}_ROOT) @@ -176,6 +183,9 @@ macro (checked_find_package pkgname) if (_pkg_REQUIRED) message (FATAL_ERROR "${ColorRed}${pkgname} is required, aborting.${ColorReset}") endif () + if (NOT _pkg_NO_RECORD_NOTFOUND) + list (APPEND CFP_ALL_BUILD_DEPS_FOUND "${pkgname} NONE") + endif () endif() if (_pkg_VERBOSE AND (${pkgname}_FOUND OR ${pkgname_upper}_FOUND OR _pkg_DEBUG)) if (_pkg_DEBUG) diff --git a/src/cmake/externalpackages.cmake b/src/cmake/externalpackages.cmake index ab559f1308..4218d5c058 100644 --- a/src/cmake/externalpackages.cmake +++ b/src/cmake/externalpackages.cmake @@ -159,6 +159,8 @@ endif() # From pythonutils.cmake find_python() +checked_find_package (pybind11 REQUIRED VERSION_MIN 2.4.2) + ########################################################################### # Dependencies for optional formats and features. If these are not found, @@ -249,12 +251,14 @@ if (NOT Ptex_FOUND OR NOT Ptex_VERSION) endif () checked_find_package (WebP) +# Note: When WebP 1.1 (released late 2019) is our minimum, we can use their +# exported configs and remove our FindWebP.cmake module. option (USE_R3DSDK "Enable R3DSDK (RED camera) support" OFF) -checked_find_package (R3DSDK) # RED camera +checked_find_package (R3DSDK NO_RECORD_NOTFOUND) # RED camera set (NUKE_VERSION "7.0" CACHE STRING "Nuke version to target") -checked_find_package (Nuke) +checked_find_package (Nuke NO_RECORD_NOTFOUND) # Qt -- used for iv @@ -313,6 +317,8 @@ macro (find_or_download_robin_map) checked_find_package (Robinmap REQUIRED) endmacro() +find_or_download_robin_map () + ########################################################################### # fmtlib @@ -360,6 +366,7 @@ macro (find_or_download_fmt) math(EXPR FMT_VERSION_MINOR "(${FMT_VERSION_NUMERIC} / 100) % 100") math(EXPR FMT_VERSION_MAJOR "${FMT_VERSION_NUMERIC} / 10000") set (fmt_VERSION "${FMT_VERSION_MAJOR}.${FMT_VERSION_MINOR}.${FMT_VERSION_PATCH}") + list (APPEND CFP_ALL_BUILD_DEPS_FOUND "${pkgname} ${${pkgname}_VERSION}") else () get_target_property(FMT_INCLUDE_DIR fmt::fmt-header-only INTERFACE_INCLUDE_DIRECTORIES) set (OIIO_USING_FMT_LOCAL FALSE) @@ -374,3 +381,6 @@ if (fmt_VERSION VERSION_EQUAL 9.1.0 AND GCC_VERSION VERSION_GREATER 0.0 AND NOT GCC_VERSION VERSION_GREATER 7.2) message (WARNING "${ColorRed}fmt 9.1 is known to not work with gcc <= 7.2${ColorReset}") endif () + +list (SORT CFP_ALL_BUILD_DEPS_FOUND COMPARE STRING CASE INSENSITIVE) +message (STATUS "All build dependencies: ${CFP_ALL_BUILD_DEPS_FOUND}") diff --git a/src/cmake/modules/FindWebP.cmake b/src/cmake/modules/FindWebP.cmake index 3c3b1e2902..38c53c6e49 100644 --- a/src/cmake/modules/FindWebP.cmake +++ b/src/cmake/modules/FindWebP.cmake @@ -17,6 +17,14 @@ # definitions giving the version: # WEBP_VERSION Version of Webp (e.g., 3.6.2) +# Try the config for newer WebP versions. +find_package(WebP CONFIG) + +if (NOT TARGET WebP::webp) +# If not found, roll our own. +# Note: When WebP 1.1 (released late 2019) is our minimum, we can use their +# exported configs and remove our FindWebP.cmake module. + include (FindPackageHandleStandardArgs) find_path (WEBP_INCLUDE_DIR webp/encode.h @@ -65,3 +73,5 @@ mark_as_advanced ( WEBP_LIBRARY WEBPDEMUX_LIBRARY ) + +endif () diff --git a/src/doc/oiiotool.rst b/src/doc/oiiotool.rst index fe4ab02d8a..c47ecca03b 100644 --- a/src/doc/oiiotool.rst +++ b/src/doc/oiiotool.rst @@ -897,6 +897,11 @@ output each one to a different file, with names `sub0001.tif`, Print timing and memory statistics about the work done by :program:`oiiotool`. +.. option:: --buildinfo + + Print information about OIIO build-time options and dependencies. + This can be useful when reporting issues. + .. option:: -a Performs all operations on all subimages and/or MIPmap levels of each diff --git a/src/include/OpenImageIO/imageio.h b/src/include/OpenImageIO/imageio.h index c414d1d345..c0f885821e 100644 --- a/src/include/OpenImageIO/imageio.h +++ b/src/include/OpenImageIO/imageio.h @@ -3107,13 +3107,30 @@ inline bool attribute (string_view name, string_view val) { /// (Added in OpenImageIO 2.5.2) /// /// - `string hw:simd` -/// - `string oiio:simd` (read-only) +/// - `string build:simd` (read-only) /// /// A comma-separated list of hardware CPU features for SIMD (and some -/// other things). The `"oiio:simd"` attribute is similarly a list of +/// other things). The `"build:simd"` attribute is similarly a list of /// which features this build of OIIO was compiled to support. /// -/// This was added in OpenImageIO 1.8. +/// These were added in OpenImageIO 1.8. The `"build:simd"` attribute was +/// added added in OpenImageIO 2.5.8 as a preferred synonym for what +/// previously was called `"oiio:simd"`, which is now deprecated. +/// +/// - `string build:platform` (read-only) +/// +/// THe name of the OS and CPU architecture that OpenImageIO was built +/// for (e.g., `"Linux/x86_64"`). (Added in OpenImageIO 2.5.8.) +/// +/// - `string build:compiler` (read-only) +/// +/// THe name and version of the compiler used to build OIIO. +/// (Added in OpenImageIO 2.5.8.) +/// +/// - `string build:dependencies` (read-only) +/// +/// List of library dependencieis (where known) and versions, separatd by +/// semicolons. (Added in OpenImageIO 2.5.8.) /// /// - `float resident_memory_used_MB` /// diff --git a/src/libOpenImageIO/CMakeLists.txt b/src/libOpenImageIO/CMakeLists.txt index e16383eb0b..aeb7b7f932 100644 --- a/src/libOpenImageIO/CMakeLists.txt +++ b/src/libOpenImageIO/CMakeLists.txt @@ -115,6 +115,16 @@ else () message (STATUS "OpenEXR core library will not be used by default") endif () +# Some extra definitions we need to retrieve build related attributes. +target_compile_definitions(OpenImageIO + PRIVATE + OIIO_FREETYPE_VERSION="${FREETYPE_VERSION_STRING}" + OIIO_OpenCV_VERSION="${OpenCV_VERSION}" + OIIO_PYTHON_VERSION="${Python_VERSION}" + OIIO_QT_VERSION="${Qt6_VERSION}${Qt5_VERSION}" + OIIO_TBB_VERSION="${TBB_VERSION}" + ) + # Source groups for libutil and libtexture source_group ("libutil" REGULAR_EXPRESSION ".+/libutil/.+") source_group ("libtexture" REGULAR_EXPRESSION ".+/libtexture/.+") @@ -126,7 +136,6 @@ target_include_directories (OpenImageIO ${OPENEXR_INCLUDES} PRIVATE ${ROBINMAP_INCLUDES} - ${FREETYPE_INCLUDE_DIRS} ) target_include_directories (OpenImageIO SYSTEM PUBLIC ${OpenCV_INCLUDES}) @@ -151,16 +160,13 @@ target_link_libraries (OpenImageIO $ $ $ + $ ${BZIP2_LIBRARIES} ZLIB::ZLIB $ ${CMAKE_DL_LIBS} ) -if (FREETYPE_FOUND) - target_link_libraries (OpenImageIO PRIVATE ${FREETYPE_LIBRARIES}) -endif() - if (WIN32) target_link_libraries (OpenImageIO PRIVATE psapi) endif() diff --git a/src/libOpenImageIO/imageio.cpp b/src/libOpenImageIO/imageio.cpp index 260da6b5bd..90fe2f0e0c 100644 --- a/src/libOpenImageIO/imageio.cpp +++ b/src/libOpenImageIO/imageio.cpp @@ -211,6 +211,64 @@ oiio_simd_caps() // clang-format on } + +static std::string +oiio_build_compiler() +{ + using Strutil::fmt::format; + + std::string comp; +#if OIIO_GNUC_VERSION + comp = format("gcc {}.{}", __GNUC__, __GNUC_MINOR__); +#elif OIIO_CLANG_VERSION + comp = format("clang {}.{}", __clang_major__, __clang_minor__); +#elif OIIO_APPLE_CLANG_VERSION + comp = format("Apple clang {}.{}", __clang_major__, __clang_minor__); +#elif OIIO_INTEL_COMPILER + comp = format("Intel icc {}", OIIO_INTEL_CLASSIC_COMPILER_VERSION); +#elif OIIO_INTEL_LLVM_COMPILER + comp = format("Intel icx {}.{}", __clang_major__, __clang_minor__); +#elif OIIO_MSVS_VERSION + comp = format("MSVS {}", OIIO_MSVS_VERSION); +#else + comp = "unknown compiler?"; +#endif + return comp; +} + + +static std::string +oiio_build_platform() +{ + std::string platform; +#if defined(__LINUX__) + platform = "Linux"; +#elif defined(__APPLE__) + platform = "MacOS"; +#elif defined(_WIN32) + platform = "Windows"; +#elif defined(__MINGW32__) + platform = "MinGW"; +#elif defined(__FreeBSD__) + platform = "FreeBSD"; +#else + platform = "UnknownOS"; +#endif + platform += "/"; +#if defined(__x86_64__) + platform += "x86_64"; +#elif defined(__i386__) + platform += "i386"; +#elif defined(_M_ARM64) || defined(__aarch64__) || defined(__aarch64) + platform += "ARM"; +#else + platform = "unknown arch?"; +#endif + return platform; +} + + + void shutdown() { @@ -417,6 +475,7 @@ attribute(string_view name, TypeDesc type, const void* val) bool getattribute(string_view name, TypeDesc type, void* val) { + using Strutil::fmt::format; if (name == "threads" && type == TypeInt) { *(int*)val = oiio_threads; return true; @@ -540,10 +599,22 @@ getattribute(string_view name, TypeDesc type, void* val) *(ustring*)val = ustring(hw_simd_caps()); return true; } - if (name == "oiio:simd" && type == TypeString) { + if ((name == "build:simd" || name == "oiio:simd") && type == TypeString) { *(ustring*)val = ustring(oiio_simd_caps()); return true; } + if (name == "build:compiler" && type == TypeString) { + *(ustring*)val = ustring(oiio_build_compiler()); + return true; + } + if (name == "build:platform" && type == TypeString) { + *(ustring*)val = ustring(oiio_build_platform()); + return true; + } + if (name == "build:dependencies" && type == TypeString) { + *(ustring*)val = ustring(OIIO_ALL_BUILD_DEPS_FOUND); + return true; + } if (name == "resident_memory_used_MB" && type == TypeInt) { *(int*)val = int(Sysutil::memory_used(true) >> 20); return true; diff --git a/src/libOpenImageIO/imageio_pvt.h.in b/src/libOpenImageIO/imageio_pvt.h.in index 45d6ed9080..1f6d061018 100644 --- a/src/libOpenImageIO/imageio_pvt.h.in +++ b/src/libOpenImageIO/imageio_pvt.h.in @@ -225,4 +225,7 @@ OIIO_NAMESPACE_END //Define a default plugin search path #define OIIO_DEFAULT_PLUGIN_SEARCHPATH "@PLUGIN_SEARCH_PATH_NATIVE@" +// List of build-time dependencies (semicolon separated) +#define OIIO_ALL_BUILD_DEPS_FOUND "@CFP_ALL_BUILD_DEPS_FOUND@" + #endif // OPENIMAGEIO_IMAGEIO_PVT_H diff --git a/src/oiiotool/oiiotool.cpp b/src/oiiotool/oiiotool.cpp index 05e8192cfa..00c25bc8b3 100644 --- a/src/oiiotool/oiiotool.cpp +++ b/src/oiiotool/oiiotool.cpp @@ -6273,10 +6273,44 @@ print_ocio_info(Oiiotool& ot, std::ostream& out) +static void +print_build_info(Oiiotool& ot, std::ostream& out) +{ + using Strutil::fmt::format; + int columns = Sysutil::terminal_columns() - 2; + + auto platform = format("OIIO {} | {}", OIIO_VERSION_STRING, + OIIO::get_string_attribute("build:platform")); + print("{}\n", Strutil::wordwrap(platform, columns, 4)); + + auto buildinfo = format(" Build compiler: {} | C++{}/{}", + OIIO::get_string_attribute("build:compiler"), + OIIO_CPLUSPLUS_VERSION, __cplusplus); + print("{}\n", Strutil::wordwrap(buildinfo, columns, 4)); + + auto hwbuildfeats + = format(" HW features enabled at build: {}", + OIIO::get_string_attribute("build:simd", "no SIMD")); + print("{}\n", Strutil::wordwrap(hwbuildfeats, columns, 4)); + + std::string libs = OIIO::get_string_attribute("build:dependencies"); + if (libs.size()) { + auto libvec = Strutil::splitsv(libs, ";"); + for (auto& lib : libvec) { + size_t pos = lib.find(':'); + lib.remove_prefix(pos + 1); + } + print(out, "{}\n", + Strutil::wordwrap("Dependencies: " + Strutil::join(libvec, ", "), + columns, 4)); + } +} + + + static void print_help_end(Oiiotool& ot, std::ostream& out) { - using Strutil::print; print(out, "\n"); int columns = Sysutil::terminal_columns() - 2; @@ -6300,28 +6334,9 @@ print_help_end(Oiiotool& ot, std::ostream& out) + Strutil::join(filternames, ", "), columns, 4)); - std::string libs = OIIO::get_string_attribute("library_list"); - if (libs.size()) { - auto libvec = Strutil::splitsv(libs, ";"); - for (auto& lib : libvec) { - size_t pos = lib.find(':'); - lib.remove_prefix(pos + 1); - } - print(out, "{}\n", - Strutil::wordwrap("Dependent libraries: " - + Strutil::join(libvec, ", "), - columns, 4)); - } + print_build_info(ot, out); - // Print the HW info - std::string buildsimd = OIIO::get_string_attribute("oiio:simd"); - if (!buildsimd.size()) - buildsimd = "no SIMD"; - auto buildinfo = Strutil::fmt::format("OIIO {} built for C++{}/{} {}", - OIIO_VERSION_STRING, - OIIO_CPLUSPLUS_VERSION, __cplusplus, - buildsimd); - print("{}\n", Strutil::wordwrap(buildinfo, columns, 4)); + // Print the current HW info auto hwinfo = Strutil::fmt::format("Running on {} cores {:.1f}GB {}", Sysutil::hardware_concurrency(), Sysutil::physical_memory() @@ -6441,6 +6456,12 @@ Oiiotool::getargs(int argc, char* argv[]) .help("Debug mode"); ap.arg("--runstats", &ot.runstats) .help("Print runtime statistics"); + ap.arg("--buildinfo") + .help("Print OIIO build information") + .action([&](cspan){ + print_build_info(ot, std::cout); + ot.printed_info = true; + }); ap.arg("--info") .help("Print resolution and basic info on all inputs, detailed metadata if -v is also used (options: format=xml:verbose=1)") .OTACTION(set_printinfo); diff --git a/src/openexr.imageio/exrinput.cpp b/src/openexr.imageio/exrinput.cpp index 1f6ef76e84..7523a2b642 100644 --- a/src/openexr.imageio/exrinput.cpp +++ b/src/openexr.imageio/exrinput.cpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include #include diff --git a/src/openexr.imageio/exrinput_c.cpp b/src/openexr.imageio/exrinput_c.cpp index cbd6f3e437..3ec78f3889 100644 --- a/src/openexr.imageio/exrinput_c.cpp +++ b/src/openexr.imageio/exrinput_c.cpp @@ -14,8 +14,6 @@ #include #include -#include - #include "exr_pvt.h" #include diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 00f7e031df..e8f733221c 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -2,9 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 # https://github.com/AcademySoftwareFoundation/OpenImageIO -# from pythonutils.cmake -checked_find_package (pybind11 REQUIRED - VERSION_MIN 2.4.2) file (GLOB python_srcs *.cpp)