【问题标题】:How to build ICU so I can use it in an iPhone app?如何构建 ICU,以便我可以在 iPhone 应用程序中使用它?
【发布时间】:2011-12-28 21:36:36
【问题描述】:

如何配置和构建 ICU,以便将其链接到我的 iPhone 应用?

我正在维护一个使用 SQLite 数据库的 iPhone 应用程序。现在我必须在启用 ICU 支持的情况下进行编译 (SQLITE_ENABLE_ICU)。我有最新的 ICU 资源。

我正在使用的 configure 标志:

./configure --target=arm-apple-darwin --enable-static --disable-shared

之后,运行gnumake 不会出错。

然后我将这些库添加到我的 Xcode 项目中。但是当我构建时,我得到了 50 行:

Undefined symbols:
  "_uregex_close_48", referenced from:
      _icuRegexpDelete in libsqlite3-cerod.a(sqlite3_cerod.o)
  "_ubrk_current_48", referenced from:
      _icuNext in libsqlite3-cerod.a(sqlite3_cerod.o)
  "_ucol_strcoll_48", referenced from:
      _icuCollationColl in libsqlite3-cerod.a(sqlite3_cerod.o)
  "_u_isspace_48", referenced from:
      _icuRegexpFunc in libsqlite3-cerod.a(sqlite3_cerod.o)
  "_utf8_countTrailBytes_48", referenced from:
      _utf8_countTrailBytes_48$non_lazy_ptr in libsqlite3-cerod.a(sqlite3_cerod.o)
     (maybe you meant: _utf8_countTrailBytes_48$non_lazy_ptr)
  "_ubrk_next_48", referenced from:
      _icuNext in libsqlite3-cerod.a(sqlite3_cerod.o)

知道我做错了什么吗?

编辑添加:

当我将库添加到项目中时(右键单击项目名称,然后添加现有...),我得到了:

ld: warning: in /Users/eric.grunin/dev/iOS/icu/source/lib/libicudata.a, file was built for unsupported file format which is not the architecture being linked (i386)
ld: warning: in /Users/eric.grunin/dev/iOS/icu/source/lib/libicui18n.a, file was built for unsupported file format which is not the architecture being linked (i386)
ld: warning: in /Users/eric.grunin/dev/iOS/icu/source/lib/libicuio.a, file was built for unsupported file format which is not the architecture being linked (i386)
ld: warning: in /Users/eric.grunin/dev/iOS/icu/source/lib/libicule.a, file was built for unsupported file format which is not the architecture being linked (i386)
ld: warning: in /Users/eric.grunin/dev/iOS/icu/source/lib/libiculx.a, file was built for unsupported file format which is not the architecture being linked (i386)
ld: warning: in /Users/eric.grunin/dev/iOS/icu/source/lib/libicutu.a, file was built for unsupported file format which is not the architecture being linked (i386)
ld: warning: in /Users/eric.grunin/dev/iOS/icu/source/lib/libicuuc.a, file was built for unsupported file format which is not the architecture being linked (i386)

这就是为什么我认为我错误地构建了库。好像在说:

  • 无法判断 .a 文件是为什么架构构建的
  • libsqlite3-cerod.a 是为 i386 构建的

我不明白这两种可能性,但我是 iPhone 开发的新手。

编辑添加

我尝试了@Sergio Moura 的解决方案,得到了我评论中提到的错误。

我尝试了@sergio 的解决方案,它成功了。但我仍然收到相同的错误,开始于:

ld: warning: in /Users/eric.grunin/dev/iOS/icu/iosbuild/lib/libicudata.a, file was built for unsupported file format which is not the architecture being linked (i386)

我可能会告诉 Xcode 错误的事情吗?我右键单击项目名称,然后选择“添加->现有文件”,然后从/icu/iosbuild/lib 中选择六个或七个.a 文件。这是正确的流程吗?

注意:

@sergio 推荐configure --host=arm-apple-darwin,@Sergio Moura 使用configure --target=arm-apple-darwin。唉,两者都没有影响。

编辑#2

定位设备(而不是模拟器)解决了除一个链接错误之外的所有问题!这是剩下的:

Undefined symbols for architecture armv6:
  "___sync_synchronize", referenced from:
      _ucol_initUCA_48 in libicui18n.a(ucol_res.ao)
      udata_getHashTable()      in libicuuc.a(udata.ao)
      _umtx_init_48 in libicuuc.a(umutex.ao)
      _initCache in libicuuc.a(uresbund.ao)
      icu_48::hasService()       in libicui18n.a(coll.ao)
      _ucol_initInverseUCA_48 in libicui18n.a(ucol_bld.ao)
      icu_48::locale_set_default_internal(char const*)in libicuuc.a(locid.ao)
      ...
ld: symbol(s) not found for architecture armv6

这之前是一连串的警告:

ld: warning: CPU_SUBTYPE_ARM_ALL subtype is deprecated: /Users/eric.grunin/dev/iOS/icu/iosbuild/lib/libicuuc.a(resbund.ao)
ld: warning: CPU_SUBTYPE_ARM_ALL subtype is deprecated: /Users/eric.grunin/dev/iOS/icu/iosbuild/lib/libicuuc.a(ustrfmt.ao)

编辑#3

@Stephen R. Loomis 建议我将 #define U_HAVE_GCC_ATOMICS1 更改为 0(在 platform.h 中)没有任何区别,唉。我还意识到错误的最后一行 (not found for architecture arm6) 并不意味着它适用于arm7,这只是一个交叉编译。当我指定 arm7 构建时,它失败并显示相同的消息。唉。

编辑#4

成功了!

总结:@sergio 的构建标志基本上是正确的。我在 ios 构建的 CFLAGS 中添加了 -DU_HAVE_GCC_ATOMICS=0。我做错的一件事是没有意识到我需要交叉编译库来创建设备版本。

我没有尝试对模拟器重复此操作,但这超出了我的问题范围。

特别感谢 Steven R. Loomis 的参与,以及 Sergio Moura 的推动。

【问题讨论】:

  • 你在 ICU 静态库中链接吗?
  • Eric,Apple 在这个解决方案上有什么问题吗?
  • Apple 没有问题,我们在 12 月发货,没有出现任何问题。

标签: iphone ios sqlite icu


【解决方案1】:

如果你有源,你真的需要链接库吗?只需将源代码添加到您的 XCode 项目中,您就可以开始使用了……

如果您真的想构建一个库,我建议您为该库创建一个以 iPhone 为目标的 XCode 项目,并将该库链接到您的代码,因为您的库是为在您的 MacOS 中运行而构建的计算机(根据您的错误日志)。

编辑

要从命令行构建它并假设您没有使用 iOS 5(因为您的 XCode 版本),我借用并调整了这组指令以正确设置标志以正确配置和构建二进制文件从here 到您的平台:

export IOS_BASE_SDK=4.2
export IOS_DEPLOY_TGT=4.2
export DEVROOT=/Developer/Platforms/iPhoneOS.platform/Developer
export SDKROOT=$DEVROOT/SDKs/iPhoneOS$IOS_BASE_SDK.sdk
export CFLAGS="-arch armv7 -pipe -no-cpp-precomp -isysroot $SDKROOT -miphoneos-version-min=$IOS_DEPLOY_TGT -I$SDKROOT/usr/include/"

export CPP=$DEVROOT/usr/bin/cpp-4.2
export CXX=$DEVROOT/usr/bin/g++-4.2
export CXXCPP=$DEVROOT/usr/bin/cpp-4.2
export CC=$DEVROOT/usr/bin/gcc-4.2
export LD=$DEVROOT/usr/bin/ld
export AR=$DEVROOT/usr/bin/ar
export AS=$DEVROOT/usr/bin/as
export NM=$DEVROOT/usr/bin/nm
export RANLIB=$DEVROOT/usr/bin/ranlib
export LDFLAGS="-L$SDKROOT/usr/lib/"

export CPPFLAGS=$CFLAGS
export CXXFLAGS=$CFLAGS

./configure --target=arm-apple-darwin --enable-static --disable-shared

请将前两条指令中的 IOS 版本正确设置为您环境的正确值。

如果您要使用 iOS 5 SDK,则需要更改编译器二进制文件的名称,因为它们已更改。

【讨论】:

  • 谢谢,我现在就试试这个。是的,我使用的是 4.3 SDK。在过去的一个小时里,我一直在尝试您的其他建议,这非常困难 - 没有简单的方法可以判断哪些源属于 lib,哪些不属于。
  • 试过你的脚本,结果附加到我的原始帖子中。
  • 抱歉,我无法进一步测试。也许是因为我的 Mac 上安装了 iOS 5,我的没有检索到任何错误,但也许你可以尝试自述文件 (icu-project.org/repos/icu/icu/trunk/…) 中提供的配置命令行,如下所示:sh /icu/source/configure --host=arm-apple-darwin --with-cross-build=/buildA --enable-static --disable-shared?
  • Sergio - 你比我早了 5 分钟。这正是他们应该做的(--host,--with-cross-build..)。 Steven,ICU 项目。
  • 哇!很高兴我的方向是正确的,因为我对ICU一无所知。 =) 这个问题刚刚向我介绍了它(现在正在阅读它的功能)
【解决方案2】:

编辑:

我可以确认,如果您这样做,正如 Steven R. Loomis 建议的那样:

  1. 在 icu/source/common/unicode/platform.h 中将 U_HAVE_GCC_ATOMICS 设置为 0

  2. 制作 distclean

  3. sh cross_configure.sh(使用我的脚本,即,如果你正在使用它)

问题应该解决了。事实上,如果不这样做,构建的库会包含有问题的未定义符号:

sergio@sfogliatella$ nm -a ./lib/libicuuc.a | grep __sync_
     U ___sync_synchronize
     U ___sync_val_compare_and_swap_4
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize
     U ___sync_synchronize

按照上述建议,这是同一命令的结果:

sergio@sfogliatella$ nm -a ./lib/libicuuc.a | grep __sync_
nm: no name list
nm: no name list

所以,毫无疑问,二进制文件中不存在违规符号。

结束编辑。

为 iOS 交叉编译 libicu 需要两个单独的步骤:

  1. 在构建目录中为您的主机 (MacOS) 编译 libicu;

  2. 通过指定交叉编译目录来交叉编译 iOS 的 libicu。

第 1 步之所以必要,是因为 libicu 会自举一点,即,它会编译一些中间工具,然后在其余的构建过程中使用这些工具;这些工具需要在主机平台上运行,因此它们是可用的。

嗯,总而言之,你可以按照步骤(1.为宿主编译):

$ cd $icu
$ mkdir hostbuild
$ cd hostbuild
$ ../icu/source/configure <configure settings you need>
$ gnumake

完成后,就可以进行交叉编译了(2. 为 iOS 编译):

$ cd $icu  (or cd ../ from the previous directory)
$ mkdir iosbuild
$ cd iosbuild
$ sh ../cross_configure_icu.sh
$ gnumake

cross_configure_icu.sh 是一个类似于上面 Sergio Moura 提出的 shell 脚本,但为 libicu 定制并使用更高级的 llvm 编译器:

DEVROOT=/Developer/Platforms/iPhoneOS.platform/Developer
SDKROOT=$DEVROOT/SDKs/iPhoneOS4.3.sdk
SYSROOT=$SDKROOT

ICU_PATH=<ABSOLUTE_PATH_TO_YOUR_ICU_DIR>
ICU_FLAGS="-I$ICU_PATH/source/common/ -I$ICU_MYSRC/source/tools/tzcode/ "

export CXXPP=
export CXXPPFLAGS=
export CPPFLAGS="-I$SDKROOT/usr/lib/gcc/arm-apple-darwin10/4.2.1/include/ -I$SDKROOT/usr/llvm-gcc-4.2/lib/gcc/arm-apple-darwin10/4.2.1/include/ -I$SDKROOT/usr/include/ -I$SDKROOT/usr/include/c++/4.2.1/armv7-apple-darwin10/ -I./include/ -miphoneos-version-min=2.2 $ICU_FLAGS"

export CFLAGS="$CPPFLAGS -pipe -no-cpp-precomp -isysroot $SDKROOT"
export CPP="$DEVROOT/usr/bin/cpp $CPPFLAGS"
export CXXFLAGS="$CFLAGS" 
export CC="$DEVROOT/usr/llvm-gcc-4.2/bin/arm-apple-darwin10-llvm-gcc-4.2"
export CXX="$DEVROOT/usr/llvm-gcc-4.2/bin/arm-apple-darwin10-llvm-g++-4.2"
export LDFLAGS="-L$SDKROOT/usr/lib/ -isysroot $SDKROOT -Wl,-dead_strip -miphoneos-version-min=2.0"

sh $ICU_PATH/source/configure --host=arm-apple-darwin --enable-static --disable-shared -with-cross-build=$ICU_PATH/hostbuild

在上面的脚本(source)中,ICU_PATH 是一个绝对路径,因为 libicu 配置所以需要 with-cross-build 选项。再次检查 SDK 和编译器的值,但这对于 4.3 应该没问题。

最后,您应该考虑到 Apple 已经(一半)拒绝了至少一个与 libicu 相关联的应用,因为它使用了保留的 API。看看这个S.O. topic

编辑:

很高兴听到您可以编译!

现在,关于链接问题。

首先,请检查 libicu 库的格式是否正确:

sergio@sfogliatella$ lipo -info ./lib/libicuuc.a 

输出应该是(对于任何库):

input file ./lib/libicuuc.a is not a fat file
Non-fat file: ./lib/libicuuc.a is architecture: arm

如果这没问题,那么下一个问题:您是为模拟器构建还是为设备构建?模拟器需要 i386 库、设备臂库...从您显示的错误消息中:

ld: 警告: ... 文件是为不受支持的文件格式构建的,它不是被链接的体系结构 (i386)

在我看来,您正在针对模拟器进行构建...为此您将需要“正常”的 macos x 库...

【讨论】:

  • 谢谢,我试试这个。我确实看到了其他评论,这有点担心,但我在这里没有太多选择。
  • @egrunin - 什么样的担心?您必须为您的平台构建一次 ICU,以便它可以运行自己的工具,然后针对您的平台构建一次。这就是它的设计方式。 —Steven,ICU 项目。
  • @Steven R. Loomis:请参阅我回答的最后一段...担心苹果拒绝 libicu... :-)
  • 缺少 tzfile.h 的巧妙解决方法 - 包括 ICU 随附的版本用于构建其时区编译器。但是,它完全不能保证与您的平台相关。
  • @sergio iOS 本身是建立在 ICU 之上的,所以如果你在 iOS 上打电话给 ICU,这可能就是拒绝的地方。请注意,我们通常不称它为 libicu,而只是称它为 ICU。 Apple 将其构建到一个库中,但这不是跨平台的默认行为。
【解决方案3】:

re: 同步同步:有人可能在原子问题上撒谎,或者需要一些 gcc 库。在 icu/source/common/unicode/uconfig.h 顶部尝试 #define U_HAVE_GCC_ATOMICS 0 (注​​意:ARM 似乎有一个弱内存模型,因此此更改将导致更多的锁定/解锁,而不是必要的,但仍然安全。)

【讨论】:

  • 这引发了错误,我认为您的意思是 /source/common/unicode/ platform.h
  • 不管在哪里。我们不会在 49 及更高版本中生成 platform.h,但它会被 configure 覆盖。所以它通常不是放置#defines的好地方。
  • 不确定我是否做错了 - 我的 platform.h 没有被覆盖。
  • 如果您在版本 49 之前的 ICU 上,它会在“配置”运行时被覆盖。在版本 49(尚未发布)中,没有动态生成 .h 文件(万岁!)。
【解决方案4】:

我使用了 iOS SDK 6.1 版,带有 clang 并针对 c++11 标准库进行构建。我发现设置诸如 CXXFLAGS 之类的环境变量没有任何影响,并且试图将它们传递给命令行上的“配置”似乎完全破坏了它。我最终制作了一个 clang、clang++ 和 ld 脚本,这些脚本可以让我传入额外的参数。比如我的 clang 脚本:

#This script circumvents gnumake getting rid of our flags. Use the environment variable $MORE_CFLAGS
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang "$@" $MORE_CFLAGS

其他相同,但在适当的地方替换clang++和ld,并分别使用MORE_CXXFLAGS和MORE_LDFLAGS。

最后,我制作了这个脚本,它执行主机构建、模拟器构建和 iOS 构建。请注意,模拟器构建包含调试信息,并且 iOS 版本是 -O2 优化的。然后它 lipos 库(制作通用二进制文件)并将它们与包含文件夹一起复制到 INSTALL_PATH 指定的目标。第 3-5 行用于配置脚本。将所有 4 个脚本放在同一个文件夹中,然后从命令行执行最后一个:

#unpack the ICU source and point $ICU_PATH at it

ICU_PATH="$HOME/Downloads/icu"
ICU_FLAGS="-I$ICU_PATH/source/common/ -I$ICU_PATH/source/tools/tzcode/ "
INSTALL_PATH="$HOME/Documents/git/gamelib/Graphics/Text/icu/51.1"
SDKROOT=`xcrun --sdk iphoneos --show-sdk-path`
SIMULATOR_SDKROOT=`xcrun --sdk iphonesimulator --show-sdk-path`


SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SAVEPATH=$PATH

cd $ICU_PATH
mkdir host_build
cd host_build
../source/configure
gnumake

PATH=$SCRIPT_DIR:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:$SAVEPATH
cd $ICU_PATH
mkdir iPhoneSimulator_build
cd iPhoneSimulator_build
#PATH points to the folder that contains this script, which should also contain scripts titled clang, clang++, and ld
#those scripts call the actual clang, clang++, and ld, appending MORE_CFLAGS, MORE_CXXFLAGS, and MORE_LDFLAGS respectively
export MORE_CFLAGS="-arch i386 -pipe -std=c99 -fmessage-length=0 -fvisibility=hidden -miphoneos-version-min=5.0 -isysroot $SIMULATOR_SDKROOT $ICU_FLAGS"
export MORE_CXXFLAGS="-arch i386 -pipe -std=c++11 -stdlib=libc++ -fmessage-length=0 -fvisibility=hidden -miphoneos-version-min=5.0 -isysroot $SIMULATOR_SDKROOT $ICU_FLAGS"
export MORE_LDFLAGS="-arch i386 -isysroot $SIMULATOR_SDKROOT -miphoneos-version-min=5.0"
$ICU_PATH/source/configure --enable-debug --disable-release --with-cross-build="$ICU_PATH/host_build" --prefix="$ICU_PATH/iPhoneSimulator_build/install" --enable-static=yes --enable-shared=no
gnumake clean
gnumake VERBOSE=1 install

PATH=$SCRIPT_DIR:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:$SAVEPATH
cd $ICU_PATH
mkdir iPhoneOS_build
cd iPhoneOS_build
#PATH points to the folder that contains this script, which should also contain scripts titled clang, clang++, and ld
#those scripts call the actual clang, clang++, and ld, appending MORE_CFLAGS, MORE_CXXFLAGS, and MORE_LDFLAGS respectively
export MORE_CFLAGS="-arch armv7 -pipe -std=c99 -O2 -fmessage-length=0 -fvisibility=hidden -miphoneos-version-min=5.0 -isysroot $SDKROOT $ICU_FLAGS"
export MORE_CXXFLAGS="-arch armv7 -pipe -std=c++11 -stdlib=libc++ -O2 -fmessage-length=0 -fvisibility=hidden -miphoneos-version-min=5.0 -isysroot $SDKROOT $ICU_FLAGS"
export MORE_LDFLAGS="-arch armv7 -isysroot $SDKROOT -miphoneos-version-min=5.0"
$ICU_PATH/source/configure --host=arm-apple-darwin --with-cross-build="$ICU_PATH/host_build" --prefix="$ICU_PATH/iPhoneOS_build/install" --enable-static=yes --enable-shared=no
gnumake clean
gnumake VERBOSE=1 install

PATH=$SAVEPATH

mkdir "$INSTALL_PATH/lib"
for file in $ICU_PATH/iPhoneOS_build/install/lib/*.a; do
    BASENAME="${file##*/}"
    lipo "$ICU_PATH/iPhoneOS_build/install/lib/$BASENAME" "$ICU_PATH/iPhoneSimulator_build/install/lib/$BASENAME" -create -output "$INSTALL_PATH/lib/$BASENAME"
done

rm -r "$INSTALL_PATH/include"
cp -r "$ICU_PATH/iPhoneOS_build/install/include" "$INSTALL_PATH/include"

【讨论】:

  • 您正在构建什么版本的 ICU 库?接受的答案适用于 49,不适用于 51。
【解决方案5】:

我现在有了解决所有这些丑陋的简单解决方案。

https://github.com/dbquarrel/icu4c-xcframework

下载这个makefile,它将获得ICU,并为(macOS,macCatalyst,iOS,iOS模拟器)构建一个ICU.xcframework for (M1/ARM/x86)。

一枪,所有的头痛都消失了。

很久以后才能回答这个问题,但这么久以前可能无法做到这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-18
    • 1970-01-01
    • 2013-10-28
    • 2021-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-19
    相关资源
    最近更新 更多