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
注意这三个步骤:
- 创建新的Metacello请求
- 配置请求(声明项目仓库地址、指定版本、基线配置、可选参数等)
- 启动加载过程
配置请求时,部分参数是必需的,部分为可选。接下来两个小节将分别介绍如何配置通过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)来查看结果列表。
评论