在现代 CMake 中定义预处理器
Defining preprocessor in modern CMake
我目前正在学习 CMake,不想养成坏习惯,因为在 CMake 中总是有两种方法可以做某事(我所见的旧方法和现代方法)
在阅读了一些关于 CMake 中的预处理器的文档并检查了这个 post 之后:
Define preprocessor macro through cmake
我得出的结论是我可以将预处理器定义为:
add_compile_definitions(FOO)
target_compile_definitions(myTarget PRIVATE FOO)
add_definitions(-DFOO)
经过一些测试后,它们实际上都按预期工作并定义 FOO
但现在我的问题是什么是我最应该使用的 "modern" 方式以及每个功能之间的真正区别是什么,我注意到的唯一区别是如果我使用 target_compile_definitions(myTarget PUBLIC FOO)
它然后在父目标中定义 FOO
。
现代 CMake 的总趋势是从全局设置转向以目标为中心的设置。仅根据此规则,target_compile_definitions()
是最现代的方法。它还允许控制设置是仅在目标中使用 (PRIVATE
),在使用此目标的其他目标中使用 (INTERFACE
),还是在两者中使用 (PUBLIC
)。在内部,它通过修改目标的属性 COMPILE_DEFINITIONS
和 INTERFACE_COMPILE_DEFINITIONS
.
来工作
接下来是现代的 add_compile_definitions()
。它将宏定义添加到当前目录和子目录中定义的所有目标;在这方面,它的范围类似于 include_directories()
。在内部,它通过修改当前目录的 COMPILE_DEFINITIONS
属性 来工作。所以:它仍然使用正确的 "modern" 机制,但它是面向目录而不是面向目标的。
在列表底部,我们有非常古老的函数 add_definitions()
。在现代 CMake 中最好避免这种情况。虽然旨在指定预处理器定义(因此得名),但它实际上允许传入任意编译器选项(这也是为什么您需要指定 -DFOO
而不是仅 FOO
作为其参数)。它试图弄清楚传入的东西是否实际上是预处理器宏定义,在这种情况下,它们被移动到目录的 COMPILE_DEFINTIIONS
属性 中。如果它们没有被识别(对于具有复杂替换字符串的宏可能会发生),它们将留在标志列表中。
的确,target_compile_definitions
是添加预处理宏的最合适的方式。
target_compile_definitions(myTarget PUBLIC FOO)
传播 FOO
到依赖项的原因也是您应该使用它的原因之一,因为您可以按目标而不是按文件夹正确定义它们。您现在可以将这些定义附加到目标,以便依赖项也声明它们。如果您尝试从不同的文件夹创建目标,您会更准确地看到这种行为,并且发现它实际上比以前非常不可靠的更有意义。
例如,在 Windows 上,所有常见的 "exports" 宏都应以这种方式声明,并且 PRIVATE
因此它们不会传播,因为它们实际上是私有定义。
我目前正在学习 CMake,不想养成坏习惯,因为在 CMake 中总是有两种方法可以做某事(我所见的旧方法和现代方法)
在阅读了一些关于 CMake 中的预处理器的文档并检查了这个 post 之后: Define preprocessor macro through cmake
我得出的结论是我可以将预处理器定义为:
add_compile_definitions(FOO)
target_compile_definitions(myTarget PRIVATE FOO)
add_definitions(-DFOO)
经过一些测试后,它们实际上都按预期工作并定义 FOO
但现在我的问题是什么是我最应该使用的 "modern" 方式以及每个功能之间的真正区别是什么,我注意到的唯一区别是如果我使用 target_compile_definitions(myTarget PUBLIC FOO)
它然后在父目标中定义 FOO
。
现代 CMake 的总趋势是从全局设置转向以目标为中心的设置。仅根据此规则,target_compile_definitions()
是最现代的方法。它还允许控制设置是仅在目标中使用 (PRIVATE
),在使用此目标的其他目标中使用 (INTERFACE
),还是在两者中使用 (PUBLIC
)。在内部,它通过修改目标的属性 COMPILE_DEFINITIONS
和 INTERFACE_COMPILE_DEFINITIONS
.
接下来是现代的 add_compile_definitions()
。它将宏定义添加到当前目录和子目录中定义的所有目标;在这方面,它的范围类似于 include_directories()
。在内部,它通过修改当前目录的 COMPILE_DEFINITIONS
属性 来工作。所以:它仍然使用正确的 "modern" 机制,但它是面向目录而不是面向目标的。
在列表底部,我们有非常古老的函数 add_definitions()
。在现代 CMake 中最好避免这种情况。虽然旨在指定预处理器定义(因此得名),但它实际上允许传入任意编译器选项(这也是为什么您需要指定 -DFOO
而不是仅 FOO
作为其参数)。它试图弄清楚传入的东西是否实际上是预处理器宏定义,在这种情况下,它们被移动到目录的 COMPILE_DEFINTIIONS
属性 中。如果它们没有被识别(对于具有复杂替换字符串的宏可能会发生),它们将留在标志列表中。
的确,target_compile_definitions
是添加预处理宏的最合适的方式。
target_compile_definitions(myTarget PUBLIC FOO)
传播 FOO
到依赖项的原因也是您应该使用它的原因之一,因为您可以按目标而不是按文件夹正确定义它们。您现在可以将这些定义附加到目标,以便依赖项也声明它们。如果您尝试从不同的文件夹创建目标,您会更准确地看到这种行为,并且发现它实际上比以前非常不可靠的更有意义。
例如,在 Windows 上,所有常见的 "exports" 宏都应以这种方式声明,并且 PRIVATE
因此它们不会传播,因为它们实际上是私有定义。