Xcode 4 无法定位静态库依赖项的公共头文件

92

辅助搜索的备选标题

  • Xcode 找不到头文件
  • Xcode 缺少 .h 文件
  • Xcode 找不到 .h 文件
  • 词法或预处理器问题:找不到文件

我正在开发一个基于 iOS 应用程序的项目,该项目最初是在 Xcode 3 上创建的。现在我已经升级到 Xcode 4,我的项目构建了多个静态库。

这些静态库还声明了公共头文件,并且应用程序代码使用这些头文件。 在 Xcode 3.x 中,这些头文件被复制(作为构建阶段)到 public headers directory,然后在应用程序项目中将 public headers directory 添加到 headers search list 中。

在 Xcode 4 中,构建目录已移动到 ~/Library/Developer/Xcode/DerivedData/my-project

问题是如何在头文件搜索设置中引用这个新位置? 看起来:

  • public headers directory 是相对于 DerivedData 目录的,但是
  • headers search 目录相对于其他内容(可能是项目位置)

我应该如何在 Xcode 4 中为 iOS 开发设置静态库目标,以确保在尝试编译依赖项时,头文件可供使用静态库的客户端?


可能与路径名有关。请查看此帖子。Xcode 4中的静态库 - Diego Marafetti
18个回答

124

我看到的解决此问题的方案要么显得不够优雅(将标头文件复制到应用程序项目中),要么过于简化以至于仅适用于微不足道的情况。

简短回答

请将以下路径添加到用户标头搜索路径中:

"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts"

这个方法为什么有效?

首先,我们需要了解问题。在正常情况下,即当您运行、测试、分析或分析性能时,Xcode会构建您的项目并将输出放入 Build/Products/Configuration/Products 目录中,该目录可通过$BUILT_PRODUCTS_DIR宏访问。

关于静态库的大多数指南都建议将公共标头文件夹路径设置为$TARGET_NAME,这意味着您的 lib 文件变成了$BUILT_PRODUCTS_DIR/libTargetName.a,而您的标头文件则被放入$BUILT_PRODUCTS_DIR/TargetName 中。只要您的应用程序在其搜索路径中包含$BUILT_PRODUCTS_DIR,则导入操作在上述四种情况下都可以正常工作。但是,在尝试归档时,此方法将无法工作。

归档的工作方式略有不同

当您归档一个项目时,Xcode会使用一个名为 ArchiveIntermediates 的不同文件夹。在该文件夹中,您将找到 /YourAppName/BuildProductsPath/Release-iphoneos/。这就是在进行归档时$BUILT_PRODUCTS_DIR指向的文件夹。如果您在其中查看,您会发现有一个符号链接指向构建的静态库文件,但标头文件所在的文件夹则缺失了。

要找到标头文件(以及 lib 文件),您需要前往 IntermediateBuildFilesPath/UninstalledProducts/。还记得您被告知设置静态库的跳过安装为YES吗?那么当您进行归档时,此设置就会生效。

顺便提一下:如果您不设置跳过安装,您的头文件将被放置在另一个位置,库文件将被复制到您的档案中,这将防止您导出可提交到App Store的.ipa文件。

经过大量搜索,我找不到与UninstalledProducts文件夹完全对应的宏,因此需要使用"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts"构建路径。

概要

对于您的静态库,请确保跳过安装,并将公共头文件放置在$TARGET_NAME中。

对于您的应用程序,请将用户头文件搜索路径设置为"$(BUILT_PRODUCTS_DIR)",这适用于常规构建,以及"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts",这适用于归档构建。


3
对我而言,使用带有递归复选框的“$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts”并没有起作用。只有当我使用不带递归标志的“$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/<nameOfStaticLibrary>”时,它才对我有效。 - TPoschel
7
请注意,*$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts* 实际上是 $(TARGET_BUILD_DIR),即使在归档时也是如此。;) - Pascal
我把这个放在“分发”下面,但是我需要把它添加到“发布”中。谢谢! - poshaughnessy
1
在我的Xcode 5中:$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/include/库名称 我没有设置为YES:始终搜索用户路径。 - xarly
这很接近:对我来说并不完全有效,但是以下内容有效:“$(TEMP_ROOT)/UninstalledProducts/$(PLATFORM_NAME)” - Michael Tyson
显示剩余10条评论

86

在我的静态库开发中,我遇到了和 Colin 回答中一样的问题,尽管 Colin 的回答很有帮助,但我还是不得不稍微修改一下才能在 Xcode 4 使用 Workspace 运行和存档项目时保持简单和一致。

我的方法的不同之处在于,您可以为所有构建配置使用单个用户头文件路径。

我的方法如下:

创建一个 Workspace

  1. 在 Xcode 4 下,转到“文件”,然后选择“新建工作区”。
  2. 从 Finder 中,您可以拖入要使用的静态库的 .xcodeproj 项目和正在构建并使用该库的新应用程序的项目。有关设置 Workspace 的更多信息,请参见 Apple 文档:https://developer.apple.com/library/content/featuredarticles/XcodeConcepts/Concept-Workspace.html

静态库项目设置

  1. 确保静态库的所有头文件都设置为复制到“Public”。这是在静态库目标 > Build Phases 中完成的。在“复制标头”阶段中,请确保所有标头都位于“Public”部分内。
  2. 接下来,转到 Build Settings,找到“Public Headers Folder Path”,并为库键入一个路径。我选择使用:

include/LibraryName

我从 RestKit 中采用了这个方法,并发现它最适合我所有的静态库。这样做是告诉 Xcode,将我们在步骤 1 中移动到“Public”标头部分中的所有标头复制到我们在此处指定的文件夹中,在构建时位于 Derived Data 文件夹内。像使用 RestKit 一样,我喜欢使用单个“include”文件夹来包含项目中使用的每个静态库。

我也不喜欢在这里使用宏,因为它将允许我们在稍后使用静态库配置项目时使用单个用户头搜索路径。

  1. 找到“Skip Install”,并确保将其设置为 YES。

使用静态库的项目设置

  1. 在 Build Phases > Link Binary With Libraries 下将静态库添加为框架,并添加要使用的静态库的 libLibraryName.a 文件。
  2. 接下来,请确保项目设置为搜索用户搜索路径。在 Build Settings > Always Search User Paths 下完成此操作,并确保其设置为 YES。
  3. 在同一区域中查找用户标头搜索路径,然后添加:

    "$(PROJECT_TEMP_DIR)/../UninstalledProducts/include"

这会告诉 Xcode 在构建过程中查找位于中间构建文件夹内的静态库。在这里,我们有“include”文件夹,我们正在使用它来设置步骤 2 中静态库项目设置的静态库位置。这是使 Xcode 正确查找静态库的最重要的一步。

配置 Workspace

在这里,我们希望配置 Workspace,以便在构建应用程序时构建静态库。这是通过编辑用于我们的应用程序的计划来完成的。

  1. 确保选择了将创建您的应用程序的计划。
  2. 从方案下拉菜单中选择“编辑方案”。
  3. 选择左侧列表顶部的“构建”。通过在中间面板上按 + 添加一个新目标。
  4. 您应该看
    import <LibraryName/LibraryName.h>
    

    这种方法可以避免为不同配置设置不同的用户头路径所带来的麻烦,因此您应该可以轻松地编译档案。

    为什么这个方法可行?

    这完全取决于这条路径:

    "$(PROJECT_TEMP_DIR)/../UninstalledProducts/include"
    

    因为我们将静态库配置为使用“跳过安装”,编译后的文件被移动到临时构建目录中的“未安装项目”文件夹中。这里的路径也解析到我们为静态库设置并用于用户头文件搜索路径的“include”文件夹。两者合作让Xcode在编译过程中知道如何找到我们的库。由于这个临时构建目录对于Debug和Release配置都存在,你只需要一个单独的路径让Xcode搜索静态库。


3
你很棒!这真的非常有帮助,而且它的效果与你所描述的一样。 - neevek
1
真棒。我应该把花送到哪里 :) - Ramesh
1
设置完成后,你们中的一些人可能会遇到 "...] unrecognized selector sent to class 0x..." 的问题。如果出现这种情况,请尝试在项目的 Targets->Project->Build Settings->Linking->Other Linker Flags 中添加以下内容:**-all_load**。 - MkVal
如果用户头搜索路径中包含空格,请确保它们用引号括起来。 - respectTheCode
在我的 Xcode 5.1 上运行正常,构建和归档成功。谢谢! - Graham Perks
显示剩余4条评论

16

Xcode 4项目无法编译静态库

相关问题:Xcode 4中的“lexical or preprocessor issue file not found”

错误可能包括:缺少头文件,“lexical or preprocessor issue”。

解决方法:

  1. 检查“用户头文件路径”是否正确。
  2. 将“始终搜索用户路径”设置为YES。
  3. 在您的项目中创建一个名为“索引标头”的组,并将标头拖到此组中,在提示时不要添加到任何目标中。

12
另一个容易被忽视但极为重要的步骤:确保您的搜索路径用双引号括起来,以避免任何空格。我总是忘记这样做。 - Brad
谢谢Brad,这确实是一个非常重要和有帮助的评论。 - Julian D.

15

这个帖子非常有用。在为自己的情况进行研究时,我发现苹果于2012年9月发布了一份名为“在iOS中使用静态库”的12页文档。这是pdf链接:http://developer.apple.com/library/ios/technotes/iOSStaticLibraries/iOSStaticLibraries.pdf

它比大多数互联网讨论要简单得多,并且对于我正在使用的外部库的配置进行了一些小的修改后,对我很有效。最重要的部分可能是:

如果您的库目标具有“复制标题”构建阶段,则应删除它;当在Xcode中执行“归档”操作时,复制标题构建阶段无法正常与静态库目标配合使用。

使用Xcode 4.4或更高版本创建的新静态库目标将具有适当配置的Copy Files阶段,因此,在创建之前应检查是否已经有一个。如果没有,请在目标编辑器底部按“添加构建阶段”,然后选择“添加复制文件”。展开新的复制文件构建阶段,并将目标设置为“产品目录”。将子路径设置为include / $ {PRODUCT_NAME}。这将把文件复制到以您的库命名(从PRODUCT_NAME构建设置中获取)的文件夹中,在内置产品目录内命名为include的文件夹中。内置产品目录中的include文件夹是应用程序的默认头搜索路径,因此这是放置头文件的适当位置。

我相信在许多现有情况下,苹果的方法可能不足够。我在这里发布它,供那些刚开始探索静态库园路的人使用-对于简单情况来说,这可能是最好的起点。


对于那些跟随上面链接的人,请注意:在创建库项目时,要求您删除虚拟模板文件的步骤。不要删除 *.pch 文件。它最终会来困扰你 + 上述建议不适用于类别。不过有一个解决方法(在某个地方看到过)。 - abbood
感谢提供官方苹果构建静态库的链接。我最初使用这个方法来构建我的静态库,但是无法进行存档。@abbood - 删除生成的pch文件有什么问题? - augusto callejas
如果您删除了.pch文件,项目将无法编译(无论是为了归档还是其他目的)。 - abbood
它对我不起作用。当我尝试使用""的导入语句时,它无法工作,我需要<>。同时,在归档时它无法找到头文件。有什么想法吗? - user1010819

4

http://developer.apple.com/library/ios/#technotes/iOSStaticLibraries/Articles/creating.html

按照苹果文档:

您的库将有一个或多个头文件,客户端需要导入这些文件。要配置向客户端导出哪些头文件,请选择您的库项目以打开项目编辑器,选择库目标以打开目标编辑器,然后选择构建阶段选项卡。如果您的库目标具有“复制头文件”构建阶段,则应将其删除;当在Xcode中执行“归档”操作时,复制头文件构建阶段无法与静态库目标正常工作。


3

4
虽然这个回答从技术上回答了问题,但是最好您在回答中包括链接文章的重要部分,并提供该链接作为参考。如果不这样做,则回答可能面临链接失效的风险。 - jscs

2
在我的情况下,我的工作空间中有几个静态库项目,其中一个项目包含依赖于其他头文件的头文件。问题出在构建顺序上。在“编辑方案”页面的“构建”部分,我取消了并行选项,并根据依赖性排列了目标的顺序,这解决了我的问题。

2

以上的答案都没有在Xcode 7上为我解决问题,但它们给了我一个好的思路。对于在Xcode 7上遇到问题的人,我通过将以下内容添加到“用户头文件搜索路径”(包括引号)中来解决了这个问题。

"$(BUILT_PRODUCTS_DIR)/usr/local/include"

根据静态库中的“公共头文件夹路径”设置,更改相对URL部分usr/local/include

2
请将$(OBJROOT)/UninstalledProducts/exactPathToHeaders添加到Header Search Paths. 出于某些原因,对我来说递归复选框无法正常工作,因此我不得不添加其余路径以查找头文件所在位置。
在Xcode的日志导航器下(断点导航器右侧的选项卡),您可以查看构建历史记录。如果选择实际构建失败,则可以展开其详细信息以查看setenv PATH并检查确保头文件路径正确。

递归复选框的注释很好,我这里也有同样的问题。 - Codezy
是的,我不得不指定一个特定的子文件夹,递归检查没有起作用。 - Oliver Pearmain
然而,我最终使用了这个路径(仅仅因为它更短):"$(PROJECT_TEMP_DIR)/../UninstalledProducts/SubProjectHeaders" - Oliver Pearmain

1

这些答案都没能解决我的问题。以下是我找到的解决方法。将以下内容(包括双引号,复制粘贴)添加到用户头文件搜索路径构建设置中:

"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/include/"

请注意相对于其他答案,此处添加了“/include/”子目录。正如其他用户所指出的那样,“recursive”选项似乎没有什么作用,因此您可以忽略它。
当以以下形式导入静态库头文件时,我的项目现在能够成功归档:
#import "LibraryName/HeaderFile.h"

除非您使用尖括号(#import <LibraryName/HeaderFile.h>)包含静态库头文件,否则不需要启用始终搜索用户路径设置,但如果这不是系统/框架头文件,则您真的不应该以这种方式进行操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接