Pharo Baseline 文档

Pharo项目通常需要通过配置来声明加载方式,这种配置通过基线(Baselines)实现。基线用于定义项目的代码包、包之间的依赖关系、与外部项目的依赖关系,以及可独立加载的子组。

为项目添加基线具有以下优势:

  • 简化项目加载流程
  • 便于其他开发者参与项目贡献
  • 让项目使用者无需关注项目依赖细节
  • 明确呈现项目依赖关系
  • 确保代码包及依赖项按正确顺序加载

本文档将阐述如何编写基线配置,以及如何加载基于该基线描述的项目。

如何定义Baseline

创建基线的首要步骤是新建一个BaselineOf的子类。在以下示例中,请将MyProject替换为您的实际项目名称:

BaselineOf subclass: #BaselineOfMyProject
  slots: {  }
  classVariables: {  }
  package: 'BaselineOfMyProject'

这个类应位于独立于项目其他代码包的专属包中。存放基线的包必须与基线类同名。总结来说,BaselineOfMyProject类应位于BaselineOfMyProject包中。

随后,创建一个用于定义项目在提交时所包含内容规范的方法。

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "The main code of the baseline will go here"
    ]

此方法名称不必限定为#baseline:,但该命名属于通用惯例。实际上,正是<baseline>编译指示(pragma)声明了该方法用于定义项目规范。

如果您的项目采用无元数据格式存储(Tonel或无元数据格式的FileTree,这是Pharo 6之后的默认设置),则需要在基线中添加以下方法:

projectClass
  ^ MetacelloCypressBaselineProject

或者,如果项目需要兼容Pharo 6.1以下版本,请使用以下版本:

projectClass
  ^ [ self class environment at: #MetacelloCypressBaselineProject ]
  on: NotFound
  do: [ super projectClass ]

该方法适用于所有采用无元数据格式的项目,且该类的返回值与基线名称无关。

这将使Metacello能够更新您的项目。由于Metacello默认的项目类原本依赖元数据来判断是否需要更新,因此这一配置是必需的。

定义构成项目的代码包

要定义项目中的代码包,需向spec发送带有包名称作为参数的#package:消息。

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests';
        package: 'MyProject-Gui';
        package: 'MyProject-Gui-Tests';
        package: 'MyProject-Examples' ]

注意:包是基线管理的最小原子单位。无法在包标签(package-tag)粒度上声明实体。

仅定义代码包不足以完成加载,因为某些包可能依赖于其他包/项目。例如,MyProject-Tests需要在MyProject之后加载。

要管理项目外部依赖关系,请参阅“定义外部依赖”章节。

对于项目内部各包之间的依赖关系,可以使用#package:with:消息向spec提供更多信息。

baseline: spec
  <baseline>
  spec
    for: #common
      do: [
        "Packages"
        spec
          package: 'MyProject';
          package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
          package: 'MyProject-Gui' with: [ spec requires: #('MyProject') ];
          package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
          package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ]

#requires:方法用于定义特定包的依赖项列表。

另一种声明依赖关系的方式是使用#includes:方法。该方法以声明集合作为参数,并会通知Metacello:如果这些依赖被加载,则当前包应包含其中所有声明。这在定义平台特定需求时非常有用,比如当我们希望某个包随平台依赖包一起加载,而该平台依赖包又依赖于当前包时。具体示例请参阅“根据Pharo版本加载不同包”章节。

定义外部依赖

定义外部依赖关系的方式因依赖项的托管位置而异。

为提高可读性,建议将依赖项的定义提取到独立的方法中。

对于其它远程Git项目

要依赖Git项目,可以使用#baseline:with:方法。

spec
  baseline: '{BaselineName}'
  with: [ spec repository: '{prefix}://{url}:{owner}/{projectName}:{version}/{subfolder}' ]

该代码段需配置以下参数:

  • {BaselineName}: 要加载的基线名称(例如,'MaterialDesignLite' 对应加载 BaselineOfMaterialDesignLite)
  • {prefix}: 托管平台标识:
    • github 对应 GitHub
    • bitbucket 对应 Bitbucket
    • gitlab 对应 GitLab
    • git 对应其他平台(此时必须提供 {url})
  • {url}: Git 托管平台基础地址。使用 git 前缀时必须提供,其他前缀可选(适用于自建 GitLab 等场景)
  • {owner}: 项目所有者(用户或组织)名称
  • {projectName}: 项目名称
  • {version}: 可选参数(默认为 master)。可以是分支名、标签(如 'v1.2.0' 或 'v1.x.x')或提交记录的 SHA
  • {subfolder}: 可选参数。当代码不在项目根目录时,指向包含代码的子文件夹路径

例如:

spec
  baseline: 'MaterialDesignLite'
  with: [ spec repository: 'github://DuneSt/MaterialDesignLite:v1.x.x/src']

依赖某个Git项目的子集

某些项目会在其基线中定义组(groups)。这些组是项目的子集,可以独立加载。上述代码段也可以进行定制,仅加载依赖项的特定组,如下所示:

spec
  baseline: 'MaterialDesignLite'
  with: [
    spec
    loads: #('Extensions');
    repository: 'github://DuneSt/MaterialDesignLite:v1.x.x/src'
  ]

定义依赖项后,将BaselineName添加到依赖于该项目的包的必需依赖项列表中。

示例:

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Dependencies"
      self materialDesignLite: spec.

      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject' 'MaterialDesignLite') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ].
materialDesignLite: spec
  spec
    baseline: 'MaterialDesignLite'
    with: [
      spec
        loads: #('Extensions');
        repository: 'github://DuneSt/MaterialDesignLite:v1.x.x/src'
    ]

依赖同一项目的不同分组

在某些情况下,您的项目可能依赖一个外部项目,但项目中的两个包分别依赖该外部项目的不同分组。

您可以使用#project:copyFrom:with:消息来创建新的依赖项配置。

materialDesignLite: spec
  spec
    baseline: 'MaterialDesignLite' with: [ spec repository: 'github://DuneSt/MaterialDesignLite:v1.x.x/src' ];
    project: 'MaterialDesignLiteExtensions' copyFrom: 'MaterialDesignLite' with: [ spec loads: #('Extensions') ]

随后即可在依赖项配置中使用新的项目名称。

示例

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Dependencies"
      self materialDesignLite: spec.

      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject' 'MaterialDesignLiteExtensions') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests' 'MaterialDesignLite' "We load the version containing MDL tests for our tests only") ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ].
materialDesignLite: spec
  spec
    baseline: 'MaterialDesignLite' with: [ spec repository: 'github://DuneSt/MaterialDesignLite:v1.x.x/src' ];
    project: 'MaterialDesignLiteExtensions' copyFrom: 'MaterialDesignLite' with: [ spec loads: #('Extensions') ]

对于本地Git项目

有时我们无法访问网络,因此需要定义对本地Git仓库的依赖。

配置方式与前文类似,但需使用以下仓库格式:

spec
  baseline: 'MaterialDesignLite'
  with: [ spec repository: 'gitlocal://full/path/to/repository' ]

对于smalltalkhub上的项目

通过#project:with:方法可依赖Smalltalkhub项目。

spec
  project: '{DependencyName}'
  with: [ spec
    className: #ConfigurationOf{ConfigurationName};
    versionString: #'{Version}';
    repository: 'http://smalltalkhub.com/mc/{owner}/{repositoryName}/main/' ]

该代码段需配置以下参数:

  • {DependencyName}:可以是您的代码包、分组及其他依赖项中的任意名称,将用于在您的包/分组中定义对该项目的依赖关系
  • {ConfigurationName}:希望引用的配置名称
  • {Version}:希望引用的版本名称,可以是'development'、'stable'、'release1'、'1.2.6'、'1.0-baseline'等形式
  • {owner}:托管项目的团队或用户名称
  • {repositoryName}:SmalltalkHub上的代码库名称

示例:

spec
  project: 'Magritte3'
  with: [ spec
    className: #ConfigurationOfMagritte3;
    versionString: #'release3';
    repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ]

与Git托管的代码库类似,您可以指定加载特定分组:

spec
  project: 'Magritte3'
  with: [ spec
    className: #ConfigurationOfMagritte3;
    versionString: #'release3';
    loads: #('Seaside');
    repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ]

现在您可以使用依赖项名称,将该项目添加为您的代码包的依赖项。

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Dependencies"
      self magritte3: spec.

      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject' 'Magritte3') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ].
magritte3: spec
  spec
    project: 'Magritte3'
    with: [ spec
      className: #ConfigurationOfMagritte3;
      versionString: #'release3';
      loads: #('Seaside');
      repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ]

分组

有时我们不需要加载完整项目,而只需加载其中部分内容,例如:

  • 仅需项目的模型部分而不需要UI(例如用于构建替代UI)
  • 仅需项目核心功能而不需要测试和示例
  • 项目包含某些可选模块
  • 等等

为处理这类情况,基线提供了分组(Group)的概念。分组是可加载的规范,仅包含项目的子集。

可以通过#group:with:消息声明分组。第二个参数定义分组的内容,内容可以是包名称、依赖项名称,甚至是另一个分组名称。

示例:

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ].

      "Groups"
      spec
        group: 'Model' with: #('MyProject');
        group: 'Tests' with: #('MyProject-Tests' 'MyProject-Gui-Tests');
        group: 'Gui' with: #('MyProject-Gui');
        group: 'Example' with: #('MyProject-Examples');
        group: 'All' with: #('Model' 'Tests' 'Gui' 'Example')

要加载包含指定分组的项目,可参阅“加载分组”章节。

默认分组

每个基线都有一个名为“default”的默认分组。该分组包含基线中声明的所有代码包和依赖项。

当使用Metacello的#load消息时,或者未指定依赖项的分组时,系统将加载“default”分组。

此分组可以被重新定义,以更改项目默认加载的内容。

示例:

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ].

      "Groups"
      spec
        group: 'default' with: #('Model' 'Gui');
        group: 'Model' with: #('MyProject');
        group: 'Tests' with: #('MyProject-Tests' 'MyProject-Gui-Tests');
        group: 'Gui' with: #('MyProject-Gui');
        group: 'Example' with: #('MyProject-Examples');
        group: 'All' with: #('Model' 'Tests' 'Gui' 'Example')

加载前/后的操作

基线提供了在加载项目时执行代码的钩子机制。

这些钩子包括:

  • #preLoadDoIt::在代码和依赖项解析并获取后,但代码加载前执行
  • #postLoadDoIt::在项目完成加载时执行

这些方法以符号作为参数,该符号应为基线类中待执行的方法名称。

这些钩子方法可包含两个可选参数:

  • 包含当前待加载项目信息的Metacello加载器
  • 包含项目规范信息的Metacello规格说明

示例:

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      spec preLoadDoIt: #'preload:package:'.
      spec postLoadDoIt: #'postload:package:'.

      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ]
preload: loader package: packageSpec

  Transcript crLog: 'The fetch was finished. Now let''s load the project'
postload: loader package: packageSpec

  Transcript crLog: 'Project loaded!'

根据Pharo版本加载不同的代码包

根据Pharo版本加载不同代码包可能非常实用。例如,如果我们有一个适用于Pharo 6的兼容包,就不希望在Pharo 7中加载它。

这可以通过不同的规格属性来实现。

到目前为止,我们一直在#common规格中定义所有内容,这适用于所有Pharo版本。但也可以为特定的Pharo版本或其他Smalltalk环境定义专属规格。

我们可以在基线中添加特殊的#for:do:指令,该指令接受特定属性作为参数。

每个Pharo版本都包含一些默认属性。对于X.Y版本的Pharo,我们拥有:

  • #pharo
  • #pharoX.x
  • #pharoX.Y.x

以Pharo 6.1为例:

  • #pharo
  • #pharo6.x
  • #pharo6.1.x

这些属性可用于定义仅在包含对应标签的镜像中执行的规格。

示例:

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ].

      spec
        for: #'pharo6.x'
        do: [ spec
          package: 'MyProject' with: [ spec requires: #('MyProject-Pharo6') ];
          package: 'MyProject-Pharo6' ].
      spec
        for: #(#'pharo3.x' #'pharo4.x' #'pharo5.x' #'pharo6.x')
        do: [ spec
          package: 'MyProject' with: [ spec requires: #('MyProject-Pharo3To6') ];
          package: 'MyProject-Pharo3To6' ] ]

在“定义项目包含的包”章节中介绍的#includes:方法,在处理平台特定需求时非常实用。假设您的MyProject包仅在存在MyProject-Pharo6时才能在Pharo 6中运行,但MyProject-Pharo6本身又依赖于MyProject。可以通过如下方式解决:

示例:

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ].

      spec
        for: #'pharo6.x'
        do: [ spec
          package: 'MyProject' with: [ spec includes: #('MyProject-Pharo6') ];
          package: 'MyProject-Pharo6' with: [ spec requires: #('MyProject') ] ].
      spec
        for: #(#'pharo3.x' #'pharo4.x' #'pharo5.x' #'pharo6.x')
        do: [ spec
          package: 'MyProject' with: [ spec requires: #('MyProject-Pharo3To6') ];
          package: 'MyProject-Pharo3To6' ] ]

自定义属性

除了Pharo自带的属性外,我们还可以定义自定义属性。

通过重写#customProjectAttributes方法,可以根据运行环境返回自定义属性。

例如:

customProjectAttributes
  Smalltalk os isMacOS ifTrue: [ ^ #(#MacOS) ].
  Smalltalk os isUnix ifTrue: [ ^ #(#Unix) ].
  Smalltalk os isWindows ifTrue: [ ^ #(#Windows) ]

随后便可在基线中使用这些自定义属性。

baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests') ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ] ].

      spec
        for: #(#'MacOS' #'Unix') do: [
          self osSubprocess: spec.
          spec package: 'MyProject' with: [ spec requires: #('OSSubprocess') ] ];
        for: #'Windows' do: [
          self processWrapper: spec.
          spec package: 'MyProject' with: [ spec requires: #('ProcessWrapper') ] ]
osSubprocess: spec
  spec
    baseline: 'OSSubprocess'
    with: [ spec repository: 'github://pharo-contributions/OSSubprocess:v1.0.1/repository' ]
processWrapper: spec
  spec
    configuration: 'ProcessWrapper'
    with: [
      spec
        versionString: '1.2';
        repository: 'http://smalltalkhub.com/mc/hernan/ProcessWrapper/main' ]

加载类型

基线支持不同的加载类型。加载类型定义了Metacello加载项目的方式。

线性加载

默认情况下,基线采用线性加载,即按顺序逐个加载包,且每个包的依赖项会先于其本身被加载。

原子加载

此加载类型强制Metacello以原子操作方式完整加载项目。当项目存在无法解析的循环依赖时特别有用。例如,对Pharo的Kernel和Collections进行原子加载就很有必要,因为它们彼此相互依赖。

要定义原子加载,需重写#project:方法。

project
  ^ super project
    loadType: #atomic;
    yourself

完整的示例

以下是一个集成了前述所有功能的完整示例:

"baseline"
baseline: spec
  <baseline>
  spec
    for: #common
    do: [
      spec preLoadDoIt: #'preload:package:'.
      spec postLoadDoIt: #'postload:package:'.

      "Dependencies"
      self materialDesignLite: spec.

      "Packages"
      spec
        package: 'MyProject';
        package: 'MyProject-Tests' with: [ spec requires: #('MyProject') ];
        package: 'MyProject-Gui' with: [ spec requires: #('MyProject' 'MaterialDesignLiteExtensions' 'Magritte3') ];
        package: 'MyProject-Gui-Tests' with: [ spec requires: #('MyProject-Tests' 'MaterialDesignLite' "We load the version containing MDL tests for our tests only") ];
        package: 'MyProject-Examples' with: [ spec requires: #('MyProject-Gui') ].

      "Groups"
      spec
        group: 'Model' with: #('MyProject');
        group: 'Tests' with: #('MyProject-Tests' 'MyProject-Gui-Tests');
        group: 'Gui' with: #('MyProject-Gui');
        group: 'Example' with: #('MyProject-Examples');
        group: 'All' with: #('Model' 'Tests' 'Gui' 'Example') ].

      spec
        for: #'pharo6.x'
        do: [ spec
          package: 'MyProject' with: [ spec includes: #('MyProject-Pharo6') ];
          package: 'MyProject-Pharo6' with: [ spec requires: #('MyProject') ] ].

      spec
        for: #(#'pharo3.x' #'pharo4.x' #'pharo5.x' #'pharo6.x')
        do: [ spec
          package: 'MyProject' with: [ spec requires: #('MyProject-Pharo3To6') ];
          package: 'MyProject-Pharo3To6' ] ].

      spec
        for: #(#'MacOS' #'Unix') do: [
          self osSubprocess: spec.
          spec package: 'MyProject' with: [ spec requires: #('OSSubprocess') ] ].

      spec
        for: #'Windows' do: [
          self processWrapper: spec.
          spec package: 'MyProject' with: [ spec requires: #('ProcessWrapper') ] ]
projectClass
  ^ MetacelloCypressBaselineProject
"dependencies"
materialDesignLite: spec
  spec
    baseline: 'MaterialDesignLite' with: [ spec repository: 'github://DuneSt/MaterialDesignLite:v1.x.x/src' ];
    project: 'MaterialDesignLiteExtensions' copyFrom: 'MaterialDesignLite' with: [ spec loads: #('Extensions') ]
"dependencies"
magritte3: spec
  spec
    project: 'Magritte3'
    with: [ spec
      className: #ConfigurationOfMagritte3;
      versionString: #'release3';
      loads: #('Seaside');
      repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ]
"dependencies"
osSubprocess: spec
  spec
    baseline: 'OSSubprocess'
    with: [ spec repository: 'github://pharo-contributions/OSSubprocess:v1.0.1/repository' ]
"dependencies"
processWrapper: spec
  spec
    configuration: 'ProcessWrapper'
    with: [
      spec
        versionString: '1.2';
        repository: 'http://smalltalkhub.com/mc/hernan/ProcessWrapper/main' ]
"accessing"
customProjectAttributes
  Smalltalk os isMacOS ifTrue: [ ^ #(#MacOS) ].
  Smalltalk os isUnix ifTrue: [ ^ #(#Unix) ].
  Smalltalk os isWindows ifTrue: [ ^ #(#Windows) ]
"actions"
preload: loader package: packageSpec

  Transcript crLog: 'The fetch was finished. Now let''s load the project'
"actions"
postload: loader package: packageSpec

  Transcript crLog: 'Project loaded!'
"accessing"
project
  ^ super project
    loadType: #atomic;
    yourself

如何使用baseline载入一个Git项目

当项目包含基线配置时,只要该项目与当前Pharo版本兼容,就可以在Pharo镜像中加载它。

下面我们将说明如何通过基线加载Git项目。

通过 Playground

加载项目的第一种方法是编程式创建Metacello请求并执行。该请求的格式如下:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'v1.x.x' path: 'src';
  baseline: 'MaterialDesignLite';
  load

注意这三个步骤:

  1. 创建新的Metacello请求
  2. 配置请求(声明项目仓库地址、指定版本、基线配置、可选参数等)
  3. 启动加载过程

配置请求时,部分参数是必需的,部分为可选。接下来两个小节将分别介绍如何配置通过Monticello和Git托管项目的加载,随后详细说明可选参数。

通过Git管理的项目

要加载Git项目,您需要执行如下表达式:

Metacello new
  repository: {repository};
  baseline: {baselineName};
  load

来自github/gitlab/bitbucket的项目

从github/gitlab/bitbucket加载项目时,repository参数格式如下:

{prefix}://{optionalHostname}:{owner}/{projectName}:{version}/{subFolder}

该代码段需配置以下参数:

  • {prefix}: 托管平台标识:
    • github 对应 GitHub
    • bitbucket 对应 Bitbucket
    • gitlab 对应 GitLab
  • {optionalHostname}: 可选服务器地址,用于私有Git服务器
  • {owner}: 项目所有者(用户或组织)名称
  • {projectName}: 项目名称
  • {version}: 可选参数,默认为master。可以是分支名、标签(如'v1.2.0'或'v1.x.x')或提交记录的SHA
  • {subfolder}: 可选参数。当代码在项目根目录时无需填写,否则指向包含代码的子文件夹路径

示例:从GitHub加载。

Metacello new
  repository: 'github://DuneSt/MaterialDesignLite:v1.x.x/src';
  baseline: 'MaterialDesignLite';
  load

示例:从私有GitLab服务器加载

Metacello new
  baseline: 'Ghost';
  repository: 'gitlab://gitlab.inria.fr:RMOD/Ghost';
  load

Metacello还提供了一些语法糖来定义GitHub或Bitbucket的代码库:

  • GitHub:使用Metacello>>githubUser:project:commitish:path:
  • Bitbucket:使用Metacello>>bitbucketUser:project:commitish:path:

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'master' path: 'src';
  baseline: 'MaterialDesignLite';
  load

来自本地仓库的项目

要从本地代码库加载项目,可使用以下格式声明代码库:

'{prefix}://{完整/路径/指向/代码库}/{子文件夹}'

该代码段需配置以下参数:

  • {prefix}: 文件格式标识:
    • filetree 对应 Filetree 项目
    • tonel 对应 Tonel 项目
  • {完整/路径/指向/代码库}: 项目在文件系统中的路径
  • {子文件夹}: 可选参数。当代码位于项目根目录时无需填写,否则应指向包含代码的子文件夹

示例:

Metacello new
  repository: 'tonel://C:\Users\Cyril\GitRepositories\Native-Browser\src';
  baseline: 'NativeBrowser';
  load

通过Smalltalkhub管理的项目

要从Smalltalkhub加载项目,需要执行如下表达式:

Metacello new
  repository: 'http://smalltalkhub.com/mc/{owner}/{repositoryName}/main';
  configuration: {configurationName};
  version: {version};
  load

该命令包含四个参数:

  • owner:托管项目的团队或用户名称
  • repositoryName:SmalltalkHub上的代码库名称
  • configurationName:要加载的配置名称。例如加载MaterialDesignLite项目时,配置名称为MaterialDesignLite,对应加载BaselineOfMaterialDesignLite类
  • version:希望引用的版本名称,可以是'development'、'stable'、'release1'、'1.2.6'、'1.0-baseline'等形式

示例:

Metacello new
  repository: 'http://smalltalkhub.com/mc/Seaside/Seaside31/main';
  configuration: 'Seaside3';
  version: #stable;
  load

您也可以使用Metacello>>smalltalkhubUser:project:方法:

Metacello new
  smalltalkhubUser: 'Seaside' project: 'Seaside31';
  configuration: 'Seaside3';
  version: #stable;
  load

无仓库项目

可以在不指定任何代码库的情况下使用Metacello。这对于在基线中定义所有项目依赖项,然后通过Metacello加载它们非常有用。

Metacello new
  baseline: #TinyBlog;
  load

载入组

有时我们只想加载项目的特定分组。这可以通过将load选择器替换为load:来实现。

load:选择器接受字符串或字符串集合作为参数。每个字符串代表基线中的一个分组名称。

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'master' path: 'src';
  baseline: 'MaterialDesignLite';
  load: 'Extensions'
Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'master' path: 'src';
  baseline: 'MaterialDesignLite';
  load: #('Extensions' 'Widgets')

冲突解决、升级与降级处理

在加载项目时可能会遇到冲突、更新或降级的情况。

例如,假设当前镜像中已存在v1.0.0版本的ProjA项目。现在要加载我们的ProjB项目,它依赖ProjA v2.0.0、ProjC v1.0.0,同时ProjD又加载了ProjC v2.0.0。

在此条件下加载ProjB将会产生两个问题:

  • ProjA需要从v1.0.0更新至v2.0.0
  • ProjC存在v1.0.0与v2.0.0之间的版本冲突

我们可以使用onConflict:、onUpgrade:和onDowngrade:选项来处理这些冲突。

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'master' path: 'src';
  baseline: 'MaterialDesignLite';
  onConflict: [ :ex | ex useIncoming ];
  onUpgrade: [ :ex | ex useIncoming ];
  onDowngrade: [ :ex | ex useLoaded ];
  load

最后一种冲突情况是:当Pharo默认发行版中已包含某个项目,而您需要加载新版本时。要处理这种情况,可以使用ignoreImage选项。

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'master' path: 'src';
  baseline: 'MaterialDesignLite';
  onConflict: [ :ex | ex useIncoming ];
  onUpgrade: [ :ex | ex useIncoming ];
  onDowngrade: [ :ex | ex useLoaded ];
  ignoreImage;
  load

以下是冲突管理的最后一个示例。Pharo社区之前使用的是名为Monticello的版本控制系统,现在大部分已迁移至GitHub。有些项目同时存在于Smalltalkhub(通过Monticello管理)和GitHub上,两者之间出现冲突并不罕见。

这里有一个小脚本,当项目名称相同时会加载通过git管理的版本:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'master' path: 'src';
  baseline: 'MaterialDesignLite';
  onConflict: [ :ex :a :b | a projectName = b projectName ifTrue: [ a projectSpec isBaselineOfProjectSpec ifTrue: [ ex useLoaded ] ifFalse: [ ex useIncoming ] ] ifFalse: [ ex resume ] ];
  load

管理告警

某些情况下项目在加载过程中会出现问题,例如加载的包缺少依赖项。此时Metacello会发出警告。大多数情况下项目仍能至少部分正常工作。若希望Metacello不弹出警告界面,可以改为记录日志。可通过使用onWarningLog或onWarning:选项来启用此功能。

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'master' path: 'src';
  baseline: 'MaterialDesignLite';
  onWarning: [ :ex | Transcript crShow: ex ];
  load
Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'master' path: 'src';
  baseline: 'MaterialDesignLite';
  onWarningLog;
  load

从Iceberg载入

Pharo 7引入了一个管理Git仓库的新工具:Iceberg。该工具允许开发者通过用户界面加载项目。

第一步是将您的Git项目添加到Iceberg中。然后右键点击项目名称,访问Metacello子菜单来加载项目。

其它特性

本节将介绍基线和Metacello的其他功能。

Metacello lock 功能

在正常的加载和升级过程中,我们通常希望加载正确版本的依赖项目。但在某些特殊情况下,自动升级可能并不合适:

  • 您可能正在积极开发某个项目的特定版本,不希望该项目被自动升级(或降级)
  • 您可能正在使用项目的git检出版本,并希望继续使用该检出版本
  • 您可能不希望特定项目被自动升级

锁定功能可以强制指定特定版本。

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'v1.1.0' path: 'src';
  baseline: 'MaterialDesignLite';
  lock

此示例将把MaterialDesignLite项目锁定在v1.1.0版本。

您可以通过以下代码片段查看已锁定的项目列表:

Metacello registry locked. "Return the list of locked projects from the Metacello registry"

Metacello image locked. "Return the list of locked projects loaded in the image."

如果您希望解锁某个项目,可以使用unlock消息。

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'v1.1.0' path: 'src';
  baseline: 'MaterialDesignLite';
  unlock

在项目加载过程中,遇到锁定时也可以执行特定操作。为此可以使用onLock:消息。

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'v1.1.0' path: 'src';
  baseline: 'MaterialDesignLite';
  onLock: [ :ex :loaded :incoming | loaded baseName = 'myProject' ifTrue: [ ex break ] ifFalse: [ ex honor ] ];
  load

Metacello get 功能

Metacello包含一个将项目基线加载到镜像中的命令。这在两种情况下特别有用:

  • 您希望仅加载项目的基线
  • 镜像中已存在过时的基线,需要在加载项目前更新它

为此可以使用get命令。

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'v1.x.x' path: 'src';
  baseline: 'MaterialDesignLite';
  get

Metacello fetch 功能

fetch命令会下载所有包但不进行加载。这包括项目本身的包及其依赖项。

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'v1.x.x' path: 'src';
  baseline: 'MaterialDesignLite';
  fetch

你也可以指定分组

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'v1.x.x' path: 'src';
  baseline: 'MaterialDesignLite';
  ignoreImage;
  fetch

Metacello record 功能

record命令的功能与fetch命令相同,但不会实际下载任何文件。因此,它可以快速报告哪些包将被加载到您的镜像中。记录结果将显示在Transcript中(可通过cmd+o+t打开)。

示例:

Metacello new
  githubUser: 'DuneSt' project: 'MaterialDesignLite' commitish: 'v1.x.x' path: 'src';
  baseline: 'MaterialDesignLite';
  record

Metacello listing 功能

list命令用于列出镜像或Metacello注册表中的项目:

Metacello image
  baseline: [ :spec | true "spec name beginsWith: 'Seaside'" ];
  list

Metacello registry
  baseline: [ :spec | true "spec name beginsWith: 'Seaside'" ];
  list

此命令需要通过查看器(Inspector)来查看结果列表。

评论

暂无评论