| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> 编程语言中引入外部代码、构建以及包管理机制的一些理解 -> 正文阅读 |
|
[Java知识库]编程语言中引入外部代码、构建以及包管理机制的一些理解 |
编程语言中引入外部代码、构建以及包管理机制的一些理解前言前一段时间和同学讨论cpp如何引入第三方代码的事儿,涉及到一些模块化和包管理机制的内容。关于这部分的内容还不甚了解,以前就想做研究一下,就利用这个月的博客做个总结,希望可以便人便己。 一、引入外部代码的方式在编写大型项目的时候,如果标准库提供的功能无法满足项目需求,一般来说都会在项目中引入第三方提供的库。不同语言在引入外部库时采取的方式各有不同,但总结一下大概有以下两种:
下面以cpp、java 和go语言为例分析一波。 1.1 C/Cpp 中引入外部代码的方式对于C和Cpp来说,在语言层次上有头文件和源文件的概念,前者负责符号的声明,后者负责符号的定义。在那个古老的年代,最初设计这种方式是因为当时的内存比较小(大概只有几十kb),编译时源码文件无法完全放入内存中,所以将符号的声明放入头文件中,把较大的源码文件按照模块化编译的原则分成多个编译单元(多个源码文件)进行单独编译,最后链接成一个可执行文件。 以信息屏蔽的视角来看,将声明和定义分开,也符合“暴露接口,隐藏细节”的原则。 在引入外部代码时,使用#include头文件的方式,将外部代码的相关声明包含在整个编译单元之中。预处理阶段将会把外部代码的声明以类似文本拷贝的方式复制到目标文件中。 如果库以“Header Only”的方式提供,那么在程序构建时,被引入的外部代码也会参与整个编译过程。这就是上面提到的方式1。 如果库除了头文件之外,还有另外的源文件,编译阶段依然会用到引入的头文件,除此之外,在链接的时候一般需要指定外部源代码编译成的二进制文件(.o目标文件或者静态、动态库文件)。这就是上面提到的方式2。 这里顺便在提一句,有的时候我们需要在linux上install软件的时候(比如说openvpn),除了编译安装openvpn本身的代码之外,还需要额外安装一些依赖的库,比如说openssl(否则就会发生各种链接错误), 其中的缘由就是上面所说的。 当然还有另外一种方式,就是把外部库的源文件和头文件一起搞下来,merge到本地的项目中,让其成为本地项目的一部分参与构建。这其实也属于上述的方式1了,但是这种方式对于小型工程来说还行,大型项目的编译时间和环境配置上可能就略显吃不消了。 1.1.1 关于cpp 20中的modulesC/CPP中使用头文件的方式有一些经常吐槽的缺点,其中重要的一点就是降低构建的速度。如果a.h 每被include一次,在预处理阶段就会被展开一次,之后膨胀的源文件在编译过程中就会让人难受的想吐。在新的cpp20的标准制定中,添加了一个新的特性-modules机制。使用了modules机制之后,在引入外部代码的时候就可以像java和go中那样,直接import,而不需要include头文件了。 对于cpp来说,还有一个槽点是cpp一直缺一个统一好用的包管理工具,其中的一个原因是使用include引入头文件的方式不利用做cpp库(包)和组件的管理工具。有了moules之后,情况应该会慢慢改善。 我没用过这个新的modules机制,但是网上不少人说这个modules机制可以提高编译的效率。但是cpp的复杂繁多的构建系统,让这个modules正式落地成熟应该还有不少路要走。 ? 1.2 Java中引入外部代码的方式在java中使用库来组织函数、数据和类,如果想引入第三方类库,直接import对应的库即可,这些库是以jar包的形式提供的,在使用时需要准备好对应的jar包。这些jar包里是用第三方源代码编译生成的.class文件,这种文件从某称程度上说也应该算是编译后的中间文件,可以归属到上述的第二种方式。 ? 答:按照我的理解,它应该即算是编译型也算是解释型的语言。从源代码到.class文件这个过程是通过编译达到的,从.class到目标机器可以执行的二进制代码是使用java虚拟机来进行解释的。 ? 1.3 Go语言中引入外部代码的方式和java类似,go中导入第三方包(库)也是使用import,不同的是Go语言中引入的包主要是通过第三方源码的形式引入的,即第三方源码会与本地代码一起编译成最终的二进制程序。也正是因为采用源码库的方式引用第三方代码, 对于一般go语言程序来说,重新编译就像是吃饭喝水,普通得不能再普通。对应于开头所述,Go引入外部代码采取的方式属于上述的方式2。为了解决编译速度问题,Go语言在设计时也做了不少权衡,如为了加快编译速度不允许循环引用。 当然,准确的说,Go也可以使用二进制的方式,通过编译成静态库的方式分发代码,但是这种方式用的比较少,这里就不多提了。 ? 二、构建和包管理机制所谓构建指的是从源码开始到成为二进制可执行程序的过程。而包管理机制在我理解就是处理整个工程项目的库依赖管理、版本控制并且和构建工具结合,更加方便的编译成可执行程序。 对于90年代之后出来的语言,比如说java、go、javascript来说,它们都有比较好的构建和包管理机制,而对于C/Cpp这种略显古老的编程语言来说,目前还没有成熟统一的构建和包管理机制。 下面依然以Cpp、Java和Go为例,简要介绍它们谈谈的构建和包管理机制。 2.1 Cpp的构建和包管理机制由于Cpp是较为贴近底层的编程语言,其构建过程和操作系统、机器架构(x86,arm等)都有关系,再加上不统一的编译器、没有模块机制等,使得其构建和包管理机制都变得比较复杂。 目前来说,Cpp的主流编译器主要有GCC、Clang和MSVC。对于比较小的demo,可以使用手动编译的方式进行构建; 对于稍大的系统,需要使用专门的构建工具,目前主要的构建系统有Make,GNU Autotools、ninja。 用的比较多的应该算是Make了。根据Make的规则,编写Makefile文件,最后来一步Make,自动执行整个构建过程; 当然,Makefile的编写也略显复杂,机械且容易出错,尤其是对于依赖较为复杂的工程项目来说。为了简化Makefile的编写的同时适配多种平台架构,又有前辈开发出来了元构建系统(meta build system)来帮助用户编写Makefile。元构建系统不会执行构建操作,而是从一个较高的层次((如CMake在CMakeLists.txt文本中)描述构建依赖关系,并转化成Makefile等底层的构建系统。一般来说,元构建系统会屏蔽掉平台相关依赖,这样元构建系统具有极好的跨平台特性。 目前主流的源构建系统由CMake、gn、QMake等。(按照我的接触范围来说,CMake用的较多些) 以上说的是Cpp的构建过程,对于包管理工具来说,由于众所周知的原因,Cpp目前并没有统一的包管理机制,目前使用较多的有cnona、vcpkg等,但是其各自都还有些问题,还未形成统一市场的格局。详细的包管理在此就不赘述了,有兴趣的看客可以参见【7】。 2.2 Java的构建和包管理机制
enen…, 太细的我也就不是很懂了╮(╯▽╰)╭。 2.3 Go的构建和包管理机制作为新时代的C语言,Go在设计之初的构建和包管理机制就不像Cpp那样麻烦和复杂(虽然一开始做的不太好)。由于google作为唯一指定官方,所以其包管理机制和语言本身贴合的更近(虽然包管理工具是外部的)。 按照网上的说法,其构建和包管理机制大概经历了三个阶段:
后记好,最后问个问题? 如果说 java js这些是运行在虚拟机之上,可以提供方便的包管理机制,那么go没有运行在虚拟机中,为什么也可以?为什么cpp没有一个好的机制呢? 你觉得呢? 参考【1】C++20 四大特性之一:Module 特性详解 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 23:42:19- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |