This page describes the technical structure of SciJava projects.
- For information on the social structure, see Governance.
- For information on the legal structure, see Licensing.
本页描述了SciJava项目的技术结构,包括ImageJ2、ImgLib2、SCIFIO以及建立在其基础上的其他项目。为了最大程度地受益,我们建议读者在阅读此处的章节之前先熟悉 Maven、Git 和 GitHub。
定义
在本文以及本 wiki 的其他地方,我们使用以下术语:
- 软件组件是一个程序,例如可重用功能的plugin或library。组件通常被设计为一起工作,并组合形成software application,例如ImageJ。在Maven术语中,组件是单个工件,通常是JAR (file format)。
- 软件项目是一个更通用的术语,指的是单个组件或相关组件的集合。例如,短语“ImageJ2 项目”指的是多个组件,包括 ImageJ Common、ImageJ Ops、ImageJ Legacy 和 ImageJ Updater。
- SciJava 组件集合 是由
pom-scijava物料清单管理的所有组件的集合。此类 SciJava 组件 驻留在多个不同的架构层中。有关详细信息,请参阅下面的“物料清单”。 - SciJava 核心组件 是 SciJava 组件层本身的 SciJava 组件。请参阅下面的“组织结构”。
- ImageJ2 软件堆栈 是构建 ImageJ2 的组件集。它包括来自SciJava、ImgLib2、ImageJ+ImageJ2和SCIFIO基础层的组件;详情请参见下面的“组织结构”和“核心库”。
SciJava 项目结构
ImageJ2项目以及SciJava软件生态系统中的相关项目都经过精心构建,以促进extensibility。
组织架构
GitHub 上有四个组织,它们构成了 SciJava 生态系统的支柱:
- scijava - 对于SciJava核心组件:通用的、非图像特定的库。
- imglib - 对于ImgLib2组件:灵活的N维图像处理。
- imagej - 用于ImageJ+ImageJ2组件:元数据丰富的图像库和应用程序。
- scifio - 对于SCIFIO组件:科学图像I/O和文件格式。
每个组织在其各自的保护伞下都包含几个相关的组件:一个核心库(见下文)和几个扩展。在社会术语中,每个组织代表由不同的team of developers开发的概念上相关的组件的集合。
其他组织可以进一步扩展此结构。例如,Fiji项目有如下几个组织:
- fiji - 用于Fiji组件
- bigdataviewer - 适用于 BigDataViewer 组件
- trakem2 - 适用于 TrakEM2 组件
此外,许多团体维护自己的 GitHub 组织,其中包含基于 SciJava 等构建的组件。以下是一些示例:
- LOCI – uw-loci
- FLIMJ – flimlib
- DAIS – TrnDy
- Florian Jug – juglab
- Stephan Saalfeld – saalfeldlab
- Stephan Preibisch – PreibischLab
- <这里是你的组织!>
Git 存储库
每个组件都包含在其自己的 Git 存储库中,以便感兴趣的开发人员可以仅挑选那些感兴趣的部分。版本控制是一个不可或缺的工具,可通过跟踪源代码的已知工作状态来确保“科学可再现性”(见下文),并保留代码随时间变化的方式和原因的书面记录。有关技术详细信息,请参阅Git部分。
为什么要单独的 Git 存储库?
使用Maven,可以创建multi-module reactor,将多个组件工件统一到单个构建中,通常在单个 Git 存储库中。 虽然许多 SciJava 组件过去都是以这种方式构建的,但我们发现,与具有单模块构建的单独 Git 存储库相比,将多个组件集中到具有多模块构建的单个 Git 存储库中具有缺点:
- 通常,多模块项目的组件都是一起版本化的,但出于 rapid iteration、extensibility 和 modularity 的原因,我们选择了单独的 versioning 组件。
- 单独的存储库使开发人员可以更轻松地仅挑选感兴趣的组件,而无需构建其余代码,因为依赖项是根据需要从远程 Maven 存储库获取的。
- 问题得到了更好的分离,每个组件都封装了自己的代码库、问题、拉取请求和技术文档。
- 由于每个组件都遵循一致的结构,因此支持工具(例如,these scripts)更易于开发和维护。
当然,也有缺点:
- 影响多个组件的更改必须作为单独的补丁集完成(即提交或拉取请求)。
- 与多个组件相关的问题必须在每个问题跟踪器中单独归档并交叉引用。
- 由于代码库分布在如此多的存储库中,因此查找感兴趣的代码可能会更加困难。
根据经验,我们发现存储在单个 Git 存储库中的多模块 Maven 项目非常适合“大爆炸”软件,该软件的版本同步并在每个 release 之前进行仔细测试,而存储在单独的 Git 存储库中的单模块项目则非常适合 RERO 式 release 范式。
Maven组件结构
这些组织中的所有组件都使用 Maven 代替 project management。每个组织都有自己的 Maven groupId。每个组件都扩展了 pom-scijava parent POM,它提供了合理的构建默认值和兼容的依赖版本(请参阅下面的“物料清单”)。
|
标识 |
项目 |
组织 |
组ID |
物料清单
pom-scijava父级包括Bill of Materials (BOM),它在其dependencyManagement section中声明了SciJava组件集合的所有组件的兼容版本。这些版本旨在在下游项目中一起使用,以防止版本偏差(其症状包括ClassNotFoundException和NoSuchMethodError,以及一般的错误行为)。当某些组件仍处于测试阶段时,此 BOM 特别重要,因为它们有时可能会违反 backwards compatibility。
核心库

The ImageJ2 software stack is composed of the following core libraries:
- SciJava Common - The SciJava application container and plugin framework.
- ImgLib2 - The N-dimensional image data model.
- ImageJ Common - Metadata-rich image data structures and SciJava extensions.
- ImageJ Ops - The framework for reusable image processing operations.
- SCIFIO - The framework for N-dimensional image I/O.
These libraries form the basis of ImageJ-based software.
The dependency hierarchy of library artifacts is shown in the diagram to the right.
Modularity
Much effort has been expended to ensure the design of these libraries provides a good separation of concerns. Developers in need of specific functionality may choose to depend on only those components which are relevant, rather than needing to add a dependency to the entire ImageJ2 software stack.
沿着这些思路,这些库煞费苦心地做到 UI 无关,不依赖于 java.awt 或 javax.swing 等包。我们的想法是,应该可以在这些库之上构建 user interface (UI),而无需更改库代码本身。我们使用不同的 UI 框架为 ImageJ 开发了多个概念验证 UI,包括 Swing、AWT、Eclipse SWT 和 Apache Pivot。
可扩展性
可扩展性是ImageJ的最大优势。 ImageJ 提供了许多不同类型的插件,并且可以使用您自己的新类型插件来扩展系统。请参阅第 CreateANewPluginType tutorial 中的说明。
SciJava Common (SJC) 库提供了带有strong typing的插件框架,并广泛使用插件本身,以允许核心功能customized easily。 SJC 拥有强大的插件发现机制,可以找到 Java 类路径上所有可用的插件,而无需提前知道它们是什么或它们位于何处。它的工作原理是在编译时通过annotation processor(受SezPoz项目启发)对插件进行索引,该annotation processor将插件元数据写入JAR文件内(在META-INF/json/org.scijava.plugin.Plugin中)。读取此索引允许系统在运行时非常快速地发现插件元数据,无需提前加载插件类。
可重复的构建
如果可以轻松地从源代码重新生成完全相同的软件应用程序,则软件版本(或构建)被称为可复制。
例如,您可以将“ImageJ 1.49g”称为可重现的构建,或Sholl Analysis 3.4.3,而引用“ImageJ”是不可重现的。
当大量使用软件库(有时称为依赖项)时,它会变得更加微妙。例如,众所周知,现已失效的 MacBiophotonics distribution of ImageJ 中的许多插件在 ImageJ 1.42l 上运行良好,但在该版本和 ImageJ 1.44e 之间停止运行。也就是说:指的是共定位分析插件并不**指的是可重现的构建,因为很难重新生成可用于验证先前发布的结果的工作共定位分析和 ImageJ 版本。
可重复构建的优点
努力实现可重复构建的一些主要原因是:
- 可重复的构建对于科学方法至关重要(参见右侧边栏)。
- 可以使用 feature branch workflow 开发风格,其中主分支始终准备好发布,甚至可以使用 continuous delivery 方法。
- Debugging with git-bisect变得可行。
- 因此,它避免了 technical debt,转而采用稳健的开发风格。
- 它吸引了更多的开发人员加入该项目,因为一切开箱即用。
SciJava 如何实现可重复的构建
由于上述原因,SciJava 软件组件力求实现可重复的构建。目标是确保今天构建和运行的代码在未来的许多年里将继续以完全相同的方式运行。
每个组件都依赖于其所有依赖项的第 versions 版本,而不是第snapshots 或version ranges。 Maven 快照是一个移动目标,依赖它会导致无法重现的构建。同样,所有使用的 Maven 插件以及父 POM 也在发布版本中声明。简而言之:所有 <version> 标签都指定发布版本,绝不是 SNAPSHOT 或 LATEST 版本。我们使用 Maven Enforcer Plugin 来强制执行此要求(尽管可以通过设置 enforcer.skip 属性暂时禁用它)。
我们有时在主题分支上临时使用 SNAPSHOT 版本。然而,我们总是在合并到 main 之前rewrite them,清除所有 SNAPSHOT 引用,以便历史记录中的所有提交都可重复构建。我们使用 SciJava 的 check-branch.sh 脚本来确保主题分支上的所有提交都干净地构建并通过测试。
在开发过程中使用快照耦合
对于并行开发多个组件,切换到 SNAPSHOT 依赖耦合可能很有用。
您可以覆盖要使用快照的依赖项的版本属性:
<properties>
<scijava-common.version>LATEST</scijava-common.version>
<enforcer.skip>true</enforcer.skip> <!-- ONLY while depending on a SNAPSHOT -->
</properties>
对于 Eclipse,您可能需要“更新 Maven 项目”才能看到快照耦合生效;选择受影响的项目时使用快捷方式⌥ Alt + F5可以快速完成此操作。
Current versions of the Eclipse Maven integration (tested with Eclipse Mars) fail to correctly resolve the LATEST version tag to SNAPSHOTs. If this happens to you, try specifying the version explicitly e.g. 2.0.0-SNAPSHOT instead of using LATEST.
Be sure to work on a topic branch while developing code in this fashion. You will need to clean up your Git history afterwards before merging things to the main branch, in order to achieve reproducible builds.
Versioning
SciJava components use the Semantic Versioning system. This scheme communicates information about the backwards compatibility (or lack thereof) between versions of each individual software component. In a nutshell:
Given a version number MAJOR.MINOR.PATCH, increment the:
- MAJOR version when you make incompatible API changes,
- MINOR version when you add functionality in a backwards-compatible manner, and
- PATCH version when you make backwards-compatible bug fixes.
See the Versioning page for a detailed discussion of SciJava versioning.