说到软件项目的依赖管理,可以从三个方面来考虑:

一、由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的更多相关文章

  1. 学习笔记——Maven实战(三)多模块项目的POM重构

    重复,还是重复 程序员应该有狗一般的嗅觉,要能嗅到重复这一最常见的坏味道,不管重复披着怎样的外衣,一旦发现,都应该毫不留情地彻底地将其干掉.不要因为POM不是产品代码而纵容重复在这里发酵,例如这样一段 ...

  2. 团队项目——编写项目的Spec

    团队项目--编写项目的Spec 一.Spec的目标        spec主要用来说明软件的外部功能,和用户的交互情况,主要用来说明软件内部的设计.图片编辑器是与生活息息相关的一个必备软件,随的流行, ...

  3. 为什么项目的jar包会和tomcat的jar包冲突?

    为什么项目的jar包会和tomcat的jar包冲突? 碰到这个问题,猜测tomcat启动时会将自己的lib和项目的lib在逻辑上归并为一个大的lib,但是并没有做版本区分以及去重,这样相同的包可能就有 ...

  4. IDEA Maven项目的Mybatis逆向工程

    IDEA Maven项目的Mybatis逆向工程 1.配置.pom 如果是在多模块开发下,该文件逆向工程要生成的那个模块下的pom文件. <build> <plugins> & ...

  5. eclipse 导入包含子maven项目的maven项目时的正确方式(父子项目)

    eclipse 导入包含子maven项目的maven项目时的正确方式(父子项目) NO1 导入时依次选择 import > Maven > Existing Maven Projects ...

  6. dajie项目的坑

    1.首先IDEA巨坑无比的地方是引入时,只要哪怕一个依赖下载不到,就会长期阻塞,删除.重新引入都没用!! 2.注释掉项目及其子项目中所有pom.xml中引用的spring仓库,否则即使maven配置阿 ...

  7. mavean项目的jar位置的影响

    由于项目的数据库需求改变了,有mysql数据库变为oracle的,那么对于项目就是需要改变数据库连接池.这个项目运用了mavean框架,那么下载jar在pom.xml文件中填写就可以了,但是oracl ...

  8. Spring Boot 不使用默认的 parent,改用自己的项目的 parent

    在初学spring boot时,官方示例中,都是让我们继承一个spring的 spring-boot-starter-parent 这个parent: <parent> <group ...

  9. 对于maven创建spark项目的pom.xml配置文件(图文详解)

    不多说,直接上干货! http://mvnrepository.com/ 这里,怎么创建,见 Spark编程环境搭建(基于Intellij IDEA的Ultimate版本)(包含Java和Scala版 ...

随机推荐

  1. jQuery代码优化:事件委托篇

    推荐阅读原文:http://www.ituring.com.cn/article/467# 推荐11收藏 随着DOM结构的复杂化和Ajax等动态脚本技术的运用,事件委托自然浮出了水面.jQuery为绑 ...

  2. JAVA 8 Optional类介绍及其源码

    什么是Optional对象 Java 8中所谓的Optional对象,即一个容器对象,该对象可以包含一个null或非null值.如果该值不为null,则调用isPresent()方法将返回true,且 ...

  3. JavaScript Patterns 6.2 Expected Outcome When Using Classical Inheritance

    // the parent constructor function Parent(name) { this.name = name || 'Adam'; } // adding functional ...

  4. lvm[12446]: Another thread is handling an event. Waiting

    在检查一Linux服务器时,发现日志里面有大量"lvm[12446]: Another thread is handling an event. Waiting..." Jul  ...

  5. mongodb的备份

    转载请附原文链接:http://www.cnblogs.com/wingsless/p/5672057.html mongodb现在为止还是没有像XtraBackup这样好用的备份工具,因此一般来说会 ...

  6. Java api 入门教程 之 JAVA的包装类

    Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数 ...

  7. Linux磁盘管理之实现多文件系统及VFS06

    待续 Linux如何支持多文件系统 不同磁盘需要不同类型的磁盘驱动程序,驱动向上提供接口,不同驱动提供的接口格式不同,在上层是块设备层,用来屏蔽下边驱动接口的差别,向上统一提供,把所有硬盘当成块设备, ...

  8. ICTCLAS中文分词库的使用

    ICTCLAS计算所中文分词(当前最好的汉语词法分析器)系统特点:准确度高(98.5%),性能优越(500KB/s分词速度),词性标注(POS tagging)且支持多种标注集,支持用户自定义词典,支 ...

  9. Configure Ocserv on CentOS 6

    Configure Ocserv on CentOS 6 Table of Contents 1. Install ocserv 2. Configure ocserv 3. How to host ...

  10. 在DigitalOcean云主机上搭建SVN服务器

    最近买了个DigitalOcean主机,顺便搭建个PPTP SVN服务器. 下面是搭建方法: https://www.digitalocean.com/community/tutorials/how- ...