问了这个问题之后,我首先提出了要求:装配系统和图形开发环境的刚性和可选性(但可取)。
我想马上指出,这并不是关于为某些特定平台(例如Android或框架)(例如Qt)编写C ++代码,该平台已经建立和编辑代码,一切准备就绪,而是关于与特定平台或平台无关的通用代码。框架。
一般:
构建系统:
- 一个可以在不同平台上构建的团队。
- 增量组装,并正确考虑所有依赖项:头文件和用于组装的第三方组件。
- 汇编脚本应仅包含特定于项目的必要最小配置。 构建的一般逻辑不应在脚本之间徘徊,而应位于构建系统或其插件中。
- 内置并行装配。
- 支持各种工具链(至少是gcc,Visual C ++,CLang)。
- 能够以最小的成本更改工具链,而无需重写整个构建脚本。
- 易于切换的构建选项:调试和发布。
- 完全不希望依赖诸如make之类的一些其他底层工具。 简而言之,组装系统应该是自给自足的。
- 非常需要将构建系统与第三方组件(例如pkg-config或用于JVM的Maven Central)的存储库集成。
- 构建系统必须可以通过插件扩展,例如 每个特定项目的组装过程可能比标准构造概念(例如代码生成或某些非标准图像的组装)更为复杂。
- 当构建脚本是某种高级编程语言甚至更好的DSL时,这很方便。 这将使您不必花很多钱,并且可以直接在脚本中直接改变构造的行为。
- 从构建脚本配置编译器和链接器时,如果系统至少提供基本的抽象,这将非常方便:例如,我想添加一个宏-为什么要考虑由哪个编译器命令行参数负责? 在MSVC上使用/ D或在gcc上使用-D-让构建系统自行解决这些无关紧要的细节。
- 与图形开发环境(IDE)的良好集成。
IDE:
- IDE正确“理解” C ++代码的能力。 IDE必须能够索引所有项目文件以及所有第三方和系统头文件和定义(定义,宏)。
- IDE应该能够自定义用于构建项目的命令,以及在何处查找头文件和定义。
- 它应该有效地帮助您键入代码,即 提供最合适的完成选项,警告语法错误等。
- 浏览大型项目应该很方便,并且可以快速,轻松地找到要使用的项目。
- 提供足够的重构机会:重命名等。
- 还需要具有生成样板代码的功能-创建新的类框架,头文件和实现文件。 getter / setter的生成,方法定义,重载虚拟方法,纯虚拟类(接口)的实现模式等。
- 突出显示并支持代码文档标签,例如Doxygen。
根据这些“愿望清单”,我考虑了几种组装系统和图形开发环境。 这篇简短的评论丝毫没有假装是完整的,并且包含了我的主观评估,但是对于某些人来说,这可能是第一步。
制造 -
[古代]乳齿象牙和当之无愧的装配系统老手,每个人都仍然不想退休,但是被迫承担越来越多的新项目。 这是一个非常低级的工具,具有自己的特定语言,在其中,如果使用空格而不是制表符,则会立即受到威胁,当场执行。 使用make,您可以做任何您想做的事情-构建任何复杂的版本,但是您必须付费以编写脚本并保持最新。 从项目到项目之间转移构建逻辑也将很昂贵。 有一些现代的化妆替代品:例如忍者和果酱,但它们并没有改变本质-它们是非常底层的工具。 就像在汇编程序中一样,您可以编写任何喜欢的东西,但这值得吗?
CMake- [中世纪]首次尝试摆脱品牌的底层细节。 但是,不幸的是,这是不可能的-此处的引擎与CMake相同,它使CMake根据具有更高级构建说明的另一个文本文件生成巨大的make文件。 Qmake的工作方式与此类似。 这种方法使我想起了一座老式木制房屋的美丽外墙,并用新鲜的塑料精心包裹。 CMake是一个稳定且经过验证的系统,甚至与Eclipse都内置了集成,但是不幸的是,它不适合我,因为它与本文开头提出的部分要求相矛盾。 在Linux下,一切似乎都很好,但是如果您需要使用MSVC在Windows下构建相同的项目-我更喜欢使用本机编译器而不是MinGW,则将生成NMake的文件。 即 对另一个工具的依赖性以及对另一个平台的不同构建命令。 当所有工作由其他“帮助者”完成时,所有这些都是结构有点弯曲的结果。
蚂蚁 -
[复兴]一种Java的克隆版本。 坦白说,我花了很多时间检查Ant(以及Maven)作为C ++的构建系统。 我立即感到,这里的C ++支持纯粹是“展示”,而没有得到充分开发。 而且,即使在Java项目中,也很少使用Ant。 作为一种脚本语言(以及Maven),在这里选择XML-这种卑鄙的鸟语:)。 乐观的事实并没有使我进一步沉浸在这个话题中。
SCons是用Python编写的
[新时代]独立的,跨平台的构建系统。 SCons在Java和C ++构建上都表现出色。 正确组装了增量组装头文件的依存关系(据我所知,使用生成元数据创建了某个数据库),并且在Windows MSVC上没有手鼓。 构建脚本语言是Python。 一个非常不错的系统,我什至想完成它的研究,但是,正如您所知,完美无极限,并且根据上述要求进行更详细的检查后发现了一些缺点。
编译器没有抽象设置,因此,例如,如果您需要更改工具链,则可能需要在构建脚本中寻找位置进行更改。 必须使用嵌套条件编写相同的宏-如果是Windows,则执行此操作,如果是GCC,则执行此类操作,等等。
不支持远程构件和一个构建在另一个构建上的高级依赖。
构建通用体系结构是为了使所谓的用户定义构建器几乎孤立地存在,并且无法通过简单的插件使用已经存在的构建逻辑来用您自己的构建逻辑进行补充。 但是总的来说,对于小型项目而言,这是一个值得选择的选择。
Gradle [现在] -我已经在Java和Kotlin项目中使用Gradle方面有丰富的经验,对此我寄予了厚望。
对于JVM语言,Gradle有一个非常方便的概念来处理构建项目所需的库(构建依赖项):
- 该脚本使用工件(例如Maven或Ivy)注册存储库的地址。 它也可以是任何其他类型/格式的存储库-如果只有一个插件。 它可以是远程存储库,某些Maven Central或您在网络上某个地方的个人托管,也可以只是文件系统上的本地代表。
- 同样,在脚本的特殊部分中,直接指示构建的依赖关系-带有版本的必要二进制工件的列表。
- 在构建之前,Gradle尝试解决所有依赖关系,并在所有存储库中查找具有给定版本的工件。 二进制文件将加载到缓存中,并自动添加到构建中。 这非常方便,我希望对于C ++,也许他们做了类似的事情。
最初,我检查了C ++支持的“旧”插件-`cpp`-并感到失望-脚本结构不直观:模型,组件,nativespec-以及来自各种类型的二进制文件的某种混合:可执行文件和库都在一个脚本中。 目前尚不清楚将单元测试放在何处。 这种结构与我用于Java的结构非常不同。
但是事实证明,还有一些新的C ++插件:cpp-application用于应用程序,cpp-library用于库:静态和动态,最后cpp-unit-test用于单元测试。 那就是我想要的! :)
默认的项目文件夹结构类似于Java项目:
- src / main / cpp-主* .cpp项目文件的根文件夹。
- src / main / headers-内部头文件的文件夹。
- src / main / public-导出标头的文件夹-库。
- src / test / cpp-测试单元* .cpp文件的文件夹。
这样的结构不是严格的-可以随时在脚本中对其进行更改,但是在没有特殊需要的情况下仍然不必这样做,这是很合理的。
顺便说一句,构建脚本通常是
build.gradle ,它是Groovy或Kotlin语言的DSL(
build.gradle.kts )可供选择。 在脚本内部,Gradle API和添加到脚本的插件的API始终可用。
对于库,您可以选择类型:静态或动态(或同时收集两个选项)。
默认情况下,配置了两个构建选项:Debug(
gradle assemble )和Release(
gradle assembleRelease )。
运行单元测试的原理与Java中的相同:gradle test将先构建主要组件,然后构建测试(如果它们位于
src / test / cpp文件夹中),然后运行测试应用程序。
臭名昭著的定义可以用抽象的方式设置-Gradle本身将生成必要的编译器选项。 还有其他一些抽象设置,例如优化,调试信息等。
开箱即用,支持GCC,Microsoft Visual C ++,CLang。
插件系统非常发达,扩展体系结构很方便-您可以采用现成的逻辑并对它进行装饰/扩展。 有两种类型的插件:动态的,直接用Groovy编写并嵌入到脚本中,或者用Java(或JVM的另一种语言)编写,然后编译成二进制构件。 对于插件,有一个免费的Gradle人工制品,任何人都可以在其中发布其插件,每个人都可以使用。 这是本文作者成功完成的:),但稍后会介绍更多。
我想更详细地介绍Gradle for C ++中使用二进制组件的系统:它几乎与Java中的相同! 依赖关系的构建几乎与我上面描述的相同。
以一个复合构建为例:
- utils-库文件夹
- app是使用utils的应用程序的文件夹。
- settings.gradle-用于将这两个组件组合为复合构建的Gradle文件。
在app文件夹中的
build.gradle文件中,编写以下依赖项就足够了:
dependencies { implementation project(':utils') }
Gradle会做剩下的! 向编译器添加路径以搜索utils头文件并链接库二进制文件。
在Linux GCC和Windows MSVC下,所有这些都同样有效。
当然,增量构建也很好用,如果您在utils中更改标头,则将重新构建该应用程序。
事实证明,Gradle走得更远,实现了将C ++工件上传到Maven存储库的能力! 为此,请使用标准的“ maven-publish”插件。
在脚本中,您需要指定要放置工件并进行gradle发布(或gradle publishToMavenLocal用于本地发布)的存储库。 Gradle将降低该项目,
以特殊格式进行布局-考虑版本,平台,体系结构和构建选项。
二进制库文件本身和公共头文件的布局-位于
src / main / public文件夹中。
很明显,您不能将C ++工件上传到Maven Cental-它不会通过强制性系统检查。 但是在网络上提升Maven存储库一点都不困难,并且您无需为本地存储库做任何事情-它只是磁盘上的一个文件夹。
现在,如果您想在项目中使用某人的库,则可以在构建脚本中编写如下内容:
repositories { maven { url = 'https://akornilov.bitbucket.io/maven' } } unitTest { dependencies { implementation 'org.bitbucket.akornilov.tools:gtest:1.8.1' } }
在这里说,对于单元测试,您需要使用
Maven存储库中的gtest工件版本1.8.1。
顺便说一下,这是一个非常真实的存储库,其中发布了我的测试版本Google Test v1.8.1,该版本使用适用于Windows和Linux x86_64的Gradle构建。
自然地,所有有关配置编译器和链接器以与Gradle的外部组件一起使用的低级工作都将进行。 您足以声明您打算将此类库与此类库中的此类版本一起使用。
为了与IDE集成,Gradle有两个用于Visual Studio和Xcode的内置插件。 它们工作得很好,除了Visual Studio插件会忽略
src / test / cpp文件夹中的单元测试代码,并只为主代码生成一个项目。
现在是时候讨论IDE以及如何使用Gradle使其成为朋友
Eclipse CDT(2018-12R)是成熟且优质的产品。 如果他成功地解析了您的项目,那么您很幸运-编辑起来会很舒服。 他很可能甚至会“理解”最困惑的汽车类型。 但是,如果没有……那么他将用红色虚线猛烈地强调所有内容,并用脏话发誓。 例如,它不会摘要标准的MSVC和Windows SDK头文件。 即使是完全无害的printf也会用红色虚线标出底线,并且不会被认为是有意义的。 还有std :: string。 在Linux下,使用他的本地gcc,一切都很好。 但是,即使试图让他从姊妹的Android Native中为项目编制索引,问题也开始出现。 在仿生头文件中,他直截了当地拒绝查看size_t的定义以及使用它的所有函数。 也许在Windows下,如果不是用Microsoft头文件来代替他,例如Cygwin或MinGW SDK,就可以纠正这种情况,但是这些技巧对我来说不是很有趣,我仍然希望该级别的软件“吃掉它们所提供的”,而不仅仅是他“爱”。
导航,重构和生成模板代码的可能性非常棒,但是在键入字母时,助手会遇到一些问题:假设我们从一些长名中键入了几个字符,为什么不提供补全选项呢? 不,助手会耐心等待直到用户到达。 或->或::。 我必须不断按Ctrl +空格键-很烦。 在Java中,可以通过选择CDT中的整个字母作为触发器来解决这种烦人的缺陷,但是我没有找到简单的解决方案。

NetBeans 8.1 / 10.0-我曾经使用过此IDE for Java,被记住是一款具有所有必要功能的优秀轻量级软件。 对于C ++,它具有一个不是由社区开发的插件,而是由NetBeans直接开发的。 对于C ++项目,对make和gcc的依赖性非常强。 代码编辑器很悠闲。 我没有在模板代码生成器中找到一个非常简单的东西:我们在类头文件中添加了一个新方法-您需要在cpp文件中生成方法主体-它不知道如何操作。 代码的“理解”程度是平均水平,似乎有些东西正在解析,但有些不是。 例如,对于他来说,使用自动迭代器在地图上进行迭代已经很困难。 他向Google Test的宏宣誓。 自定义build命令是有问题的-在具有gcc并使其可用的Linux上(尽管已经在使用另一个构建系统),该命令可以运行,在Windows上则需要MinGW,但是即使这样做,它也会拒绝构建。 通常,可以使用C ++在NetBeans中工作,但我觉得这不太舒服;我可能需要真正喜欢这种环境,以免注意到它的各种麻烦。

KDevelop 5.3.1-曾经被认为是KDE(Linux)的开发人员工具,但是现在有Windows的版本。 它具有一个快速且令人愉悦的代码编辑器,具有精美的语法突出显示(基于Kate)。 篡改左侧的构建系统将不起作用-对他而言,主要构建系统是CMake。 它可以忍受MSVC和Windows SDK头文件,在任何情况下,printf和std :: string都不完全导致类似Eclipse CDT的stupor。 编写代码的快速帮助者-键入时几乎立即提供良好的完成选项。 它有一个生成模板代码的有趣机会:您可以编写自己的模板并将其放在网上。 从模板创建时,可以连接到现成模板的数据库并下载所需的模板。 唯一让人不高兴的是:用于创建新类的内置模板在Windows和Linux下均无法正常工作。 创建类的向导具有多个窗口,您可以在其中配置很多东西:需要哪些构造函数,类的哪些成员等。 但是在Windows的最后阶段,会及时弹出某种错误以确认其文本是不可能的,并且创建了两个文件h和cpp,其大小为1个字节。 在Linux中,由于某种原因,您不能选择构造函数-选项卡为空,并且仅在输出上正确生成头文件。 通常,这种成熟产品的儿童疾病看起来有些轻浮。

QtCreator 4.8.1(开放源代码版本) -也许,听说过这个名字后,您很困惑如何用一个带有钩子的千兆字节分发工具将这个怪物囚禁在Qt下。 但这是通用项目环境的“简化”版本。 他的发行版仅重约150 MB,不包含特定于Qt的内容:
download.qt.io/official_releases/qtcreator/4.8 。
实际上,他可以快速,正确地完成我在需求中编写的几乎所有内容。 它解析Windows和Linux的标准标头,针对任何构建系统对其进行自定义,建议完成选项,方便地生成新的类,方法主体,允许重构和代码导航。 如果您只是想舒适地工作,而不必不断思考如何解决这个问题,那么看一下QtCreator是有意义的。


实际上,仍然需要谈论Gradle中我没有足够的东西来充分发挥作用:与IDE集成。 为了使系统为IDE本身生成项目文件(已在其中写入用于构建项目的命令),列出了所有源文件,需要路径来搜索头文件并确定。
为此,我
为Gradle`cpp-ide-generator`编写了一个
插件,并发布在Gradle插件门户上。
该插件只能与“ cpp-application”,“ cpp-library”和“ cpp-unit-test”一起使用。
这是在
build.gradle中使用它的示例:
plugins { id 'cpp-library' id 'maven-publish' id 'cpp-unit-test' id 'org.bitbucket.akornilov.cpp-ide-generator' version '0.3' } library {
该插件支持与上述所有图形开发环境的集成,但是在插件配置块-ide中,您可以禁用对不必要的IDE的支持:
kdevelop = false
如果将
autoGenerate参数设置为true,则将在构建过程中自动生成所有允许的IDE的项目文件。 同样,在自动生成模式下,清理构建时将删除项目文件:
gradle clean 。
支持增量生成,即 只有那些需要真正更新的文件才会被更新。
以下是插件添加的目标列表:
- generateIde-为所有允许的IDE生成项目文件。
- cleanIde-删除所有允许的IDE的项目文件。
- generateIde [name]-使用给定名称(必须允许使用IDE)为IDE生成项目文件,例如generateIdeQtCreator。
- 可用名称:Eclipse,NetBeans,QtCreator,KDevelop。
- cleanIde [name]-删除具有给定名称的IDE项目文件,例如cleanIdeQtCreator。
在生成期间,插件会“嗅探”构建,并从中提取所有必要的信息以创建项目文件。 打开项目后,所有源文件应在IDE中可见,所有标头的路径应被注册,并且还应配置用于配置/ build / clear的基本构建命令。
我要做的第二个插件称为
“ cpp-build-tuner” ,它还可以与cpp-application,cpp-library和cpp-unit-test协同工作。
该插件没有设置,仅需上传即可:
plugins { id 'cpp-library' id 'maven-publish' id 'cpp-unit-test' id 'org.bitbucket.akornilov.cpp-build-tuner' version '0.5' }
该插件使用工具链的设置(编译器和链接器)针对不同的构建选项(调试和发布)执行一些小的操作。 支持MSVC,gcc,CLang。
对于MSVC尤其如此,因为默认情况下,作为发行版本的结果,您将获得带有粗体信息和静态链接的标准库的“粗体”而不是美观的二进制。 我“侦察”了Visual Studio本身中MSVC的部分设置,默认情况下会将其添加到其C ++项目中。 对于gcc / CLang和MSVC,“发布”配置文件中均包含链接时间优化。
注意:插件已经使用Gradle v5.2.1的最新版本进行了测试,并且未测试与先前版本的兼容性。可以查看插件的源代码,以及将Gradle用于库的简单示例:静态和动态,以及使用它们的应用程序:
bitbucket.org/akornilov/tools next gradle / cpp。
这些示例还显示了如何使用Google Test进行单元测试。
内置于Gradle中的带有Google Test v1.8.1的Maven存储库(无模拟)。UPD:Windows版本的
QtCreato r早于
4.6.2 (并且至少在编写这些
行时 ,包括
4.10 以内 ),已经“忘记了如何”理解MSVC SDK。 所有的std ::空间都用红色下划线标出,并拒绝索引。 因此,目前,4.6.2版最适合在Windows下使用。
已经发布了新版本的
cpp-build-tuner
v1.0插件(
cpp-ide-generator
v0.5是次要的改进)。
1)在
cpp-build-tuner
添加了配置块。
buildTuner { lto = false gtest = '1.8.1' libraries { common = ['cutils.lib'] windows = ['ole32', 'user32'] linux = ['pthread', 'z'] } libDirs.common = ['../build/debug', '../release'] }
lto (布尔值)-为发行版本启用或禁用LTO。 默认启用。
gtest (字符串)-添加了对单元测试的Google测试支持。 当前,GCC,MinGW-W64和MSVC仅支持1.8.1版。
库 (容器)-用于链接的库列表。 容器内有三个字段(行列表):common-适用于任何平台的库,
windows
仅适用于Windows和
linux
仅适用于Linux。
libDirs (容器)-用于使用链接器搜索库的文件夹列表。 容器结构与库列表相同。
2)添加了为
cpp-application
运行应用
cpp-application
。 该插件为此为此项目添加了其他任务:
run
,
runDebug
(与
run
相同)和
runRelease
。 任务分别取决于
assemble
,
assembleDebug
和
assembleRelease
。
与标准Java应用程序插件一样,您可以在启动时传递命令行参数:
gradle run --args="arg1 arg2 ..."
。
UPD与托管插件的更改有关,该组已更改:
plugins { id 'loggersoft.cpp-build-tuner' version '1.1' id 'loggersoft.cpp-ide-generator' version '0.5' }
新项目地址:
gradle-cpp.sourceforge.io说明文件:
sourceforge.net/p/gradle-cpp/wiki/cpp-build-tunersourceforge.net/p/gradle-cpp/wiki/cpp-ide-generator