OSX +自制软件+ CMake + libpng版本不匹配问题

时间:2016-04-09 23:22:52

标签: c++ macos cmake homebrew

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

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),当我使用-I构建时,VERBOSE=1声明永远不会出现在我的构建行中。但是,如果我使用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?我不能简单地卸载自制libpng,因为我的其他一些软件包依赖它,包括该程序使用的其他库。

编辑:作为临时解决方法,我刚刚将/usr/local/include添加到INCLUDE_DIRS()并添加了libpng16/png.h,但这是一个脆弱的黑客攻击。< / p>

2 个答案:

答案 0 :(得分:4)

今天偶然发现了这个令人毛骨悚然的错误,并花了一些时间来解决它。 问题是经典的cmake风格的Find * .cmake分别搜索标头和库-在某些情况下结果可能不匹配。 MacOS具有特殊的框架,在默认情况下会在其他位置之前进行搜索,从而加剧了问题。

在我的情况下,cmake从/Library/Frameworks/Mono.framework中找到标头,这些标头当然已经过时了,根本没有任何库。

您可以选择:

  1. 设置(CMAKE_FIND_FRAMEWORK LAST) 这解决了像这样的流氓框架的问题(在我的情况下) 这是快速的脏修复程序。

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

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

  4. 在您的存储库中包含libpng的副本并使用它

答案 1 :(得分: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变体时:您可以看到详细的build命令的输出是什么问题:

compile命令如下:

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

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