OSX + homebrew + CMake + libpng 版本不匹配问题

Posted

技术标签:

【中文标题】OSX + homebrew + CMake + libpng 版本不匹配问题【英文标题】:OSX + homebrew + CMake + libpng version mismatch issue 【发布时间】:2016-07-31 03:39:14 【问题描述】:

我在使用 CMake 在 OSX 上构建 C++ 项目时遇到了一个相当奇怪的问题,同时将 libpng 作为依赖项引入。我通过自制软件和以下 CMake 规则安装了 libpng 1.6.21:

FIND_PACKAGE(PNG REQUIRED)
INCLUDE_DIRECTORIES($PNG_INCLUDE_DIRS)
LINK_DIRECTORIES($PNG_LIBRARY_DIRS)
ADD_DEFINITIONS($PNG_DEFINITIONS)

当 CMake 开始构建并找到依赖项时,它会输出:

-- Found PNG: /usr/local/lib/libpng.dylib (found version "1.4.12") 

进一步调查,/usr/local/lib/libpng.dylib 是 brew 1.6 版本的符号链接:

$ ls -l /usr/local/lib/libpng.dylib 
lrwxr-xr-x  1 fluffy  admin  40 Apr  9 16:06 /usr/local/lib/libpng.dylib -> ../Cellar/libpng/1.6.21/lib/libpng.dylib

但是,似乎包含不正确的png.h,因为在启动时打印出PNG_LIBPNG_VER_STRING 输出1.4.12。当然,当我尝试运行我的程序时,我得到一个版本不匹配并且库无法工作:

libpng warning: Application built with libpng-1.4.12 but running with 1.6.21
libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: [write_png_file] png_create_write_struct failed

使用FIND_PACKAGE(PNG),当我使用VERBOSE=1 构建时,-I 声明永远不会出现在我的构建行中。但是,如果我使用 PkgConfig 方法:

FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(LIBPNG libpng16 REQUIRED)
INCLUDE_DIRECTORIES($LIBPNG_INCLUDE_DIRS)
LINK_DIRECTORIES($LIBPNG_LIBRARY_DIRS)
LINK_LIBRARIES($LIBPNG_LIBRARIES)
ADD_DEFINITIONS($LIBPNG_DEFINITIONS)

确实出现了正确的 -I 标志,但它仍在使用系统 png.h 而不是 Homebrew。

有没有办法强制编译器使用自制的png.h?我不能简单地卸载 homebrew libpng,因为我的一些其他软件包依赖于它,包括该程序使用的其他库。

编辑:作为临时解决方法,我刚刚将/usr/local/include 添加到我的INCLUDE_DIRS() 并包含libpng16/png.h,但这是一个脆弱的hack。

【问题讨论】:

CMake compile options for libpng 可能重复 @joel 这不是重复的,这是 OSX 特有的问题;这个问题的答案是什么对我不起作用。 您的问题与平台无关 @Joel 但事实并非如此。 OSX 提供了系统 libpng,homebrew 提供了不同的版本。并查看链接的答案并将其与我在这里使用的 CMake 片段进行比较... @S.S.Anne 不幸的是,不,自从我发布这个问题以来的四年里,我没有做任何 C++ 和 libpng 的东西。听到这个问题仍然存在,有点令人沮丧。 【参考方案1】:

今天偶然发现了这个令人愤怒的错误,并花了一些时间来解决它。 问题是经典的 cmake 风格 Find*.cmake 分别搜索头文件和库 - 在某些情况下,结果可能和将不匹配。 MacOS 通过具有特殊情况的框架夸大了这个问题,默认情况下在其他位置之前被搜索。

在我的例子中,cmake 从 /Library/Frameworks/Mono.framework 中找到标头,这些标头当然已经过时并且根本没有库。

您可以选择:

    设置(CMAKE_FIND_FRAMEWORK LAST) 这解决了像这样的流氓框架的问题(在我的例子中) 这是快速的脏修复。

    像原来一样使用 PackageConfig - 这是推荐的长期解决方案。 使用 PackageConfig 可防止 lib/headers/flags 不匹配。 您唯一应该做的就是确保在系统路径之前将包含路径传递给编译器。

    删除有问题的框架/路径/库(例如 zlib 有时也会打包 libpng!)

    在您的 repo 中包含 libpng 的副本并使用它

【讨论】:

2.对于具有 pkg​​-config 的系统来说,这似乎是最好的方法,我想我会为非 Windows 切换到该方法 如果 pkg-config 不起作用,我最终使用 FindPNG 作为备用。谢谢!【参考方案2】:

要求

标头版本与用于构建 libpng 库的版本匹配 在赏金描述中也提到,该解决方案不应涉及 PkgConfig

由于 PkgConfig 通常是首选解决方案,因此提供了两种解决方案 - 一种使用 PkgConfig,另一种不使用 PkgConfig。

第一个需求可以用一些 C 代码表示,如下所示:

#include <stdio.h>
#include <png.h>

int main(void) 
    printf("libpng version of lib (%u):%s", 
           png_access_version_number(), 
           png_get_header_version(NULL));
    printf("used libpng version in app (%d):%s", 
           PNG_LIBPNG_VER, 
           PNG_HEADER_VERSION_STRING);
    return 0;

构建

要获得一些调试输出,您可以使用创建应用程序的小脚本:

#!/bin/zsh
rm -rf build
cmake -B build
cmake --build build -v
build/png_app

使用 PkgConfig 的解决方案

cmake_minimum_required(VERSION 3.17)
project(png_app C)

set(CMAKE_C_STANDARD 99)

find_package(PkgConfig REQUIRED)
pkg_check_modules(PNG libpng16 REQUIRED)

add_executable(png_app main.c)
target_include_directories(png_app PRIVATE $PNG_INCLUDE_DIRS)
target_link_directories(png_app PRIVATE $PNG_LIBRARY_DIRS)
target_link_libraries(png_app $PNG_LIBRARIES)

测试解决方案 1

libpng 是这样安装自制软件的:

brew install libpng

程序运行的输出是:

libpng version of lib (10637): libpng version 1.6.37 - April 14, 2019
used libpng version in app (10637): libpng version 1.6.37 - April 14, 2019

所以我们可以看到它按预期工作!标头版本和使用的库版本匹配。

注意:在调试输出中我们可以看到,cc 被调用

.../cc  -I/usr/local/Cellar/libpng/1.6.37/include/libpng16 ...

链接是这样发生的:

.../cc  ... main.c.o -o png_app -L/usr/local/Cellar/libpng/1.6.37/lib -Wl,-rpath,/usr/local/Cellar/libpng/1.6.37/lib -lpng16 -lz

注意:如果在构建过程中遇到错误:

Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE)

然后用 brew 安装它:

brew install PkgConfig

解决方案 2

第二种解决方案是使用硬编码路径而不使用 PkgConfig。如果安装了新版本的库,则必须调整 PNG_BASEPATH。

cmake_minimum_required(VERSION 3.17)
project(png_app C)

set(CMAKE_C_STANDARD 99)

set(PNG_BASEPATH /usr/local/Cellar/libpng/1.6.37)
set(PNG_LIBRARIES png16)

add_executable(png_app main.c)
target_include_directories(png_app PRIVATE $PNG_BASEPATH/include)
target_link_directories(png_app PRIVATE $PNG_BASEPATH/lib)
target_link_libraries(png_app $PNG_LIBRARIES)

为什么 find_package 不起作用?

当我尝试使用 OP 问题中的 find_package 变体时:您可以看到详细构建命令的输出有什么问题:

编译命令如下:

.../cc  -I/Library/Frameworks/Mono.framework/Headers ... -o .../main.c.o -c .../main.c

因此它尝试使用 /Library/Frameworks/Mono.framework 中的 libpng14.14,而不是 homebrew 安装的版本。

【讨论】:

嗯,也许我误以为我不应该用 pkgconfig 来做【参考方案3】:

我在 github Actions 上设置 CI 时遇到了同样的问题。

最后,Mono 再次发生冲突。在尝试使用各种标志解决此冲突几个小时后,我仍然在构建和运行时之间存在差异。对于那些没有本地 MacO 进行调试和测试的人,这里有一个快速的解决方案: https://gist.github.com/nicerobot/1515915

它会删除单声道。它没有触及其他任何东西,问题解决了。请不要为其他任何事情这样做,这是解决问题的最丑陋的方法。 :)

【讨论】:

以上是关于OSX + homebrew + CMake + libpng 版本不匹配问题的主要内容,如果未能解决你的问题,请参考以下文章

Homebrew PHP5.5失败OSX

自 Mojave 以来,链接在 homebrew 的 cmake 中不起作用

OSX Homebrew 安装 Spring Boot CLI

Cmake 无法在 Homebrew 中找到 boost_pyhton 库

在 OSX 10.8.2 - PHP 5.3.15 上安装 mcrypt 和 homebrew

在 OSX 10.8.2 - PHP 5.3.15 上安装 mcrypt 和 homebrew