控制 Xcode 将包含哪个项目头文件
我的 Xcode 项目使用两个目标构建同一产品的变体。两者之间的区别仅在于使用所包含库的版本。对于 .c 源文件,可以使用目标复选框轻松将正确的版本分配给正确的目标。但是,包含的头文件始终包含相同的文件。这对于一个目标来说是正确的,但对于另一个目标来说是错误的。
有没有办法控制每个目标包含哪个头文件?
这是我的项目文件层次结构(在 Xcode 中复制):
MyProject
TheirOldLib
theirLib.h
theirLib.cpp
TheirNewLib
theirLib.h
theirLib.cpp
myCode.cpp
myCode.cpp 执行以下操作:
#include "theirLib.h"
…
somecode()
{
#if OLDVERSION
theirOldLibCall(…);
#else
theirNewLibCall(…);
#endif
}
当然,我为一个目标定义 OLDVERSION
,而不是另一个目标。
请注意,#include
必须如图所示。以下两个操作都会失败,并出现文件未找到错误:
#include "TheirOldLib/theirLib.h"
#include "TheirNewLib/theirLib.h"
那么有没有办法告诉 Xcode 每个目标要包含哪个 irLib.h
?
限制条件:
- 两个头文件具有相同的名称。作为最后的手段,我可以重命名其中一个,但我宁愿避免这样做,因为这会导致其他平台上的重大问题。
- 必须更改 #include
以添加对封闭文件夹的引用也是我宁愿避免的事情,因为我需要使用条件编译指令执行两次。
- 我可以随意调整我的项目,因为我认为合适,
谢谢您的任何帮助。
My Xcode project builds to variations of the same product using two targets. The difference between the two is only on which version of an included library is used. For the .c source files it's easy to assign the correct version to the correct target using the target check box. However, including the header file always includes the same one. This is correct for one target, but wrong for the other one.
Is there a way to control which header file is included by each target?
Here is my project file hierarchy (which is replicated in Xcode):
MyProject
TheirOldLib
theirLib.h
theirLib.cpp
TheirNewLib
theirLib.h
theirLib.cpp
myCode.cpp
and myCode.cpp does thing such as:
#include "theirLib.h"
…
somecode()
{
#if OLDVERSION
theirOldLibCall(…);
#else
theirNewLibCall(…);
#endif
}
And of course, I define OLDVERSION
for one target and not for the other.
Note the #include
must be as shown. Both of the following fail with a file not found error:
#include "TheirOldLib/theirLib.h"
#include "TheirNewLib/theirLib.h"
So is there a way to tell Xcode which theirLib.h
to include per target?
Constraints:
- the two header files have the same name. As a last resort, I could rename one of them, but I'd rather avoid that as this will lead to major hair pulling on the other platforms.
- having to change the #include
to add a reference to the enclosing folder is also something I'd rather avoid, because I would need to do it twice with a conditional compile directive.
- I'm free to tweak my project as I otherwise see fit
Thanks for any help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
答案的关键部分是按照 Chris 在评论中的建议使用 USE_HEADERMAP = NO 。以下是详细信息。
简短秘诀(在 Xcode 3.2.2 中检查):
为每个相关目标添加 USE_HEADERMAP = NO 的自定义构建设置。方法如下:
1.1.在“构建”窗格中打开目标的信息面板。
1.2.下拉窗口左下角的操作弹出菜单,选择“添加用户定义的设置”。
1.3.在新添加的行中,将第一列(“Setting”)设置为
USE_HEADERMAP
,将第二列(“Value”)设置为NO
。为每个目标添加正确的包含路径(目标构建设置“标头搜索路径”)。在我的例子中,这将是:
2.1.为“旧”目标添加
TheirOldLib
2.2.为“新”目标添加
TheirNewLib
步骤 1 禁用 Xcode 的自动头映射功能,通过该功能,项目中包含的任何头文件都可以通过其名称直接访问,无论其名称如何实际路径。当两个标头具有相同的名称时,此功能会导致无法解决的歧义。
步骤 2 允许
#include "theirLib.h"
在不限定头文件实际路径名的情况下工作。这两个步骤一起满足了我的两个限制。
最后,据我所知,Apple 没有记录
USE_HEADERMAP
。我将为此填写一份错误报告,因为此设置在许多情况下至关重要,正如谷歌搜索所揭示的那样。报告为 rdar://7840694。也在开放雷达上 http://openradar.appspot.com/radar?id=253401The key part of the answer is to use USE_HEADERMAP = NO as suggested by Chris in a comment. Here are the details.
Short recipe (checked in Xcode 3.2.2):
add a custom build setting of USE_HEADERMAP = NO for each concerned target. Here is how:
1.1. Open the target's info panel on the "Build" pane.
1.2. Pull down the action pop-up menu at the bottom left of the window, select "Add User-Defined Setting".
1.3. In the newly added line, set the first column ("Setting") to
USE_HEADERMAP
, and the second column ("Value") toNO
.add the correct include path to each target (target Build settings "Header Search Paths"). In my example that would be:
2.1. add
TheirOldLib
for "old" target2.2. add
TheirNewLib
for "new" targetStep 1 disables the automatic header map feature of Xcode, through which any header file included in the project is directly accessible through its name, whatever its actual path. When two headers have the same name, this feature leads to an unresolvable ambiguity.
Step 2 allows for the
#include "theirLib.h"
to work without qualifying the header file actual path name.These two steps together fulfill my two constraints.
Finally,
USE_HEADERMAP
is not documented by Apple, as far as I can tell. I'll fill a bug report for that, as this setting is crucial in a number of cases, as googling for it reveals. Reported as rdar://7840694. Also on open Radar as http://openradar.appspot.com/radar?id=253401USE_HEADERMAP=NO
对于某些项目来说是多余的。仅使用HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT=NO
可能就足够了。文档在这里:
https://developer.apple。 com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html
USE_HEADERMAP=NO
is overkill for some projects. It might be enough to just useHEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT=NO
.Documentation here:
https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html
Xcode 通过创建头映射文件来加速构建。
您还可以向编译器提供头映射文件,而不是向编译器提供搜索头的目录列表。标头映射文件就像一个哈希表,查找键是
include
参数,值是标头的路径。以下是此类地图文件的示例:
(注意: 这不是头映射文件的实际语法,它只是人类可读的表示)
这三个条目匹配
现在 Xcode 有三个设置来控制生成头映射文件。
HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT
如果
YES
(默认),属于正在构建的目标的所有头文件都将添加到头映射文件中,并且可以使用include "header.h"
包含。请注意,标头只能属于框架/库/捆绑包目标,而不能属于应用程序/程序目标。HEADERMAP_INCLUDES_FRAMEWORK_ENTRIES_FOR_ALL_PRODUCT_TYPES
如果
YES
(默认),所有其他目标的标头都会添加到标头映射文件中,并且可以使用include
包含。请注意,对于非框架目标也是如此。HEADERMAP_INCLUDES_PROJECT_HEADERS
如果
YES
(默认),则构建目标所属的项目文件中存在的所有其他标头也会添加到标头映射文件中,并且可以使用include "header.h".
此外,还有一个通用设置
USE_HEADERMAP
,它控制是否应生成头映射文件。仅当YES
(默认)时,Xcode 才会生成头映射文件并将其作为参数传递给编译器。如果标头未在标头映射文件中列出或未使用标头映射,编译器将使用两种搜索策略之一搜索标头:
如果使用
<...> 导入标头;
,它将在使用-I
选项(HEADER_SEARCH_PATHS
)指定的所有目录中搜索,使用-isystem
指定的所有目录选项(Xcode 中的SYSTEM_HEADER_SEARCH_PATHS
),以及 stanadrd 系统头目录(所选 SDK 的/usr/include
以及属于已安装开发人员工具的其他目录);完全按照该顺序,并且在每个类别中都按照给定的顺序。如果使用
"..."
导入标头,它将在与正在构建的 .c/.m 文件相同的目录中以及使用-iquote< 指定的所有目录中进行搜索/code> 选项(Xcode 中的
USER_HEADER_SEARCH_PATHS
),并在同一目录中搜索<...>
;完全按照该顺序,并且在每个类别中都按照给定的顺序。Xcode speeds up building by creating header map files.
Instead of providing the compiler with a list of directories where to search for headers, you can also provide it with header map files. A header map file is like a hashtable, the lookup keys are the
include
arguments, the values are the paths to the headers.Here is an example for such a map file:
(Note: This is not the actual syntax of a header map file, it's just a human readable representation)
These three entries match
Now Xcode has three settings that control the generation of header map files.
HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT
If
YES
(default), all header files that belong to the target being built are added to the header map file and can be included usinginclude "header.h"
. Note that headers can only belong to framework/library/bundle targets, not to application/program targets.HEADERMAP_INCLUDES_FRAMEWORK_ENTRIES_FOR_ALL_PRODUCT_TYPES
If
YES
(default), headers of all other targets are added to the header map file and can be included usinginclude <TargetName/header.h>
. Note that this is also true for non-framework targets.HEADERMAP_INCLUDES_PROJECT_HEADERS
If
YES
(default), all other headers present in the project file the built target belongs to are also added to the header map file and can be included usinginclude "header.h"
.Additionally there is the general setting
USE_HEADERMAP
that controls if a header map file shall be generated at all. Only ifYES
(default), Xcode will generate a header map file and pass it as argument to the compiler.In case a header is not listed in a header map file or header maps aren't used, the compiler will search for the headers using one of two search strategies:
In case the header is imported with
<...>
, it will search in all directories specified with the-I
option (HEADER_SEARCH_PATHS
), all directories specified with the-isystem
option (SYSTEM_HEADER_SEARCH_PATHS
in Xcode), and in the stanadrd system header directories (/usr/include
of the selected SDK and other directories belonging to the installed developer tools); in exactly that order and within each category in the order given.In case the header is imported with
"..."
, it will search in the same directory as the .c/.m file being built, in all directories specified with the-iquote
option (USER_HEADER_SEARCH_PATHS
in Xcode), and in the same directories it searches for<...>
; in exactly that order and within each category in the order given.为什么不能在每个目标中使用不同的包含路径?
Why can't you just use different include paths in each target?
使用 USE_HEADERMAP=NO 并在“用户标头搜索路径”中首先包含您的自定义目录,然后递归地包含项目目录: ${PROJECT_DIR}/TheirNewLib ${PROJECT_DIR}/**
Use USE_HEADERMAP=NO and in "User Header Search Paths" include your custom directory first and project directory recursively second: ${PROJECT_DIR}/TheirNewLib ${PROJECT_DIR}/**