谈谈软件项目的dependency
说到软件项目的依赖管理,可以从三个方面来考虑:
一、由build system控制的dependency
现在的build system,都支持一定程度上的dependency management, 比如make支持target之间的dependency,ant也支持其每个target之间的dependency(区别是make的每个非PHONY的target是个文件,make会检查输入与输出之间的timestamp来达到incremental build的效果,而ant则是对上一次build没有任何记忆,除了javac task支持incremental compile)
上面的dependency讲的主要是如何确定build的顺序,但dependency管理的另一个非常重要的目标,是自动获取dependency,并设置好:
- 对于C++来讲:includedir(-I), libdir(-L)
- 对于Java来讲:classpath
手工的维护这些path,非常麻烦而且容易出错,因为我们在一个过低的level管理这些信息 - 事实上,正确的做法应该是:用户只需声明我依赖这个component或者我依赖这个library,dependency manager自动帮我设置好这些path。
总的来讲,dependency manager有这么几个作用:
- 让用户在合适的逻辑层次声明dependency,自动设置好需要的path
- Resolve transitive dependency
- Conflict manage (diamond dependency)
这些功能都不是手工能够解决的。
二、不由build system控制的dependency
如果有两个或者多个不同的project,由不同的team开发,甚至使用了不同的build system,那么基本上是不可能把他们放在同一个build pipeline中编译的。但是他们之间又有dependency,该怎么处理?
这里其实就是一个升级的问题, 比如:
B -> A/1.0
而A还是不断的开发,可能会改变已有的实现(引起B的运行时错误),也可能改变已有的接口(引起B的编译时错误),如果B在一段时间之后升级到A的新版本A/1.1,B可能需要很长时间做migration - 或者说integration。其实从continuous integration的角度来理解这个问题,正确的做法应该是:
A要尽可能快的推出一个新的release,而B要立马跟上,这样每次B的改动都非常小,不容易出错,出了错也容易解决。
但推出一个新的release毕竟不是件小事,很多team会有自己的担心:资源不够,risk太高等等 - 所以上述是一个比较理想的情况。在达到那个情况之前,可以另外有个方案:
B继续使用A/1.0, 但是同时B新开出一个branch:B/edge,依赖于最新的A的代码,A的每次build,都会trigger B/edge的build (edge build),这样保证B总是build against最新的A,任何问题都可以在第一时间发现,并fix
关于edge build,值得另外写一篇文章讨论一下。
这里,这种不由build system控制的dependency,就需要由其他系统来控制,比如各种CI server中的job,一个job会trigger另外一个job,也是一种dependency关系,刚好对应起来。
三、dependency graph的显示
软件项目之间的dependency graph,很好的反映了各个项目之间的关系,注意这个dependency的单位不是project,而是release,这更动态的反映出了依赖关系,同时,由于你知道每个项目的dependency,根据这个信息就能建出一张完整的dependency graph,从而能得出所有consumer的信息,于是,我就有了关于这个项目的所有dependency和consumer的信息:
- 我用了那些项目,用的版本会不会太老?
- 我被哪些项目用了?受不受欢迎?是不是可以停止支持某个release了(如果没有太多consumer的话)
显示一个项目的dependency时,可以有以下几部分:
- 直接dependency
- 间接dependency
- 直接consumer
- 间接consumer
- dependency graph
关于dependency graph,要注意的是:首先它是一张完整的图,正确的显示了所有直接dependency和间接dependency,但是这样的图,对于一个大的项目来讲的话,会比较乱 - 考虑某个项目即是直接dependency,又是间接dependency的情况,就会有多个箭头指向它。所以一般显示的时候,会将其的transitive reduction显示出来(tred,dot自动做reduction),在保证其reachbility的同时,减少边的数量,使图看起来比较简练:
![]()
![]()
还有要注意的一点是,我们需要提供额外的metadata,来保证这个图的正确性。以ivy为列,ivy.xml提供了比较完整的dependency信息(没有类似机制的build system至少要产生这种metadata),但其问题是对于有dependency conflict的情况(diamond dependency),如上面第二张图,如果b和c依赖于不同版本的d,单凭ivy.xml无法确认到底选d的那个版本,这是ant/ivy在build过程当时选择一定的conflict manager方式确定下来的,而这个信息,必须以某种方式告诉dependency graph - 一般的方式就是在build过程中产生metadata并予以保存。
对于没有ivy.xml这种机制的build system,需要产生metadata一保存:
- 所有直接的dependency
- 有conflict的间接dependency
然后可以根据所有项目的这个信息产生正确的dependency graph
谈谈软件项目的dependency的更多相关文章
- 学习笔记——Maven实战(三)多模块项目的POM重构
重复,还是重复 程序员应该有狗一般的嗅觉,要能嗅到重复这一最常见的坏味道,不管重复披着怎样的外衣,一旦发现,都应该毫不留情地彻底地将其干掉.不要因为POM不是产品代码而纵容重复在这里发酵,例如这样一段 ...
- 团队项目——编写项目的Spec
团队项目--编写项目的Spec 一.Spec的目标 spec主要用来说明软件的外部功能,和用户的交互情况,主要用来说明软件内部的设计.图片编辑器是与生活息息相关的一个必备软件,随的流行, ...
- 为什么项目的jar包会和tomcat的jar包冲突?
为什么项目的jar包会和tomcat的jar包冲突? 碰到这个问题,猜测tomcat启动时会将自己的lib和项目的lib在逻辑上归并为一个大的lib,但是并没有做版本区分以及去重,这样相同的包可能就有 ...
- IDEA Maven项目的Mybatis逆向工程
IDEA Maven项目的Mybatis逆向工程 1.配置.pom 如果是在多模块开发下,该文件逆向工程要生成的那个模块下的pom文件. <build> <plugins> & ...
- eclipse 导入包含子maven项目的maven项目时的正确方式(父子项目)
eclipse 导入包含子maven项目的maven项目时的正确方式(父子项目) NO1 导入时依次选择 import > Maven > Existing Maven Projects ...
- dajie项目的坑
1.首先IDEA巨坑无比的地方是引入时,只要哪怕一个依赖下载不到,就会长期阻塞,删除.重新引入都没用!! 2.注释掉项目及其子项目中所有pom.xml中引用的spring仓库,否则即使maven配置阿 ...
- mavean项目的jar位置的影响
由于项目的数据库需求改变了,有mysql数据库变为oracle的,那么对于项目就是需要改变数据库连接池.这个项目运用了mavean框架,那么下载jar在pom.xml文件中填写就可以了,但是oracl ...
- Spring Boot 不使用默认的 parent,改用自己的项目的 parent
在初学spring boot时,官方示例中,都是让我们继承一个spring的 spring-boot-starter-parent 这个parent: <parent> <group ...
- 对于maven创建spark项目的pom.xml配置文件(图文详解)
不多说,直接上干货! http://mvnrepository.com/ 这里,怎么创建,见 Spark编程环境搭建(基于Intellij IDEA的Ultimate版本)(包含Java和Scala版 ...
随机推荐
- asp.net mvc 之旅—— 第二站 窥探Controller下的各种Result
平时我们在Action中编码的时候,我们都知道所有的Action返回值类型都是ActionResult,并且我们的返回值也是各种奇葩,比如:Json(),Content(), View()等等...当 ...
- Java并发之CountDownLatch 多功能同步工具类
package com.thread.test.thread; import java.util.Random; import java.util.concurrent.*; /** * CountD ...
- Rac grid用户启停监听报错无权限
[grid@max1 ~]$ lsnrctl stop LSNRCTL for Linux: Version 11.2.0.3.0 - Production on 04-NOV-2016 00:20: ...
- 学习myBatis - 如何配置myBatis
这篇文章主要学习如何配置myBatis. 要学习新东西要讲究方法,要从三个层面去理解它:它是什么(what),为什么要学它(why),怎么用它(how).有了学习方法学习的效率才高. 1.myBati ...
- Linux学习--------二
Linux基础知识 Linux文件系统为一个倒转的单根树状结构文件系统的根为"/" 文件系统严格区分大小写路径 使用"/"分割(windows使用"\ ...
- [转]VS2010中如何创建一个WCF
本文转自:http://www.cnblogs.com/zhangliangzlee/archive/2012/08/28/2659701.html 转载:http://www.cnblogs.com ...
- [转]backbone.js 示例 todos
本文转自:http://www.css88.com/doc/backbone/examples/todos/index.html <!DOCTYPE html> <html lang ...
- .Net程序员之Python基础教程学习----字符串的使用 [Second Day]
在The FirstDay 里面学习了列表的元组的使用,今天开始学习字符串的使用.字符串的使用主要要掌握,字符串的格式化(C语言中我们应该都知道,Python和C语言差别不大),字符串的基本 ...
- 一个优秀的Unity3d开发者必备的几种设计模式
Unity脚本编程 众所周知,unity的编程属于脚本化,脚本没有一个具体的概念跟架构, 导致在项目过程中,经常出现哪里需要实现什么功能,就随便添加脚本, 结果,就造成了一片混乱,不好管理. 更有甚者 ...
- FragmentTransaction.replace() 你不知道的坑
一.起源: 先看效果,在linearLayout中添加了4个Fragment,然后点击替换一次确替换了两个Fragment,引发了我的研究兴趣: 第一次启动 ...