上传代码
定义
Julia有两种代码加载机制。
-
包含代码,例如’include("source.jl")'。 启用允许您将程序拆分为多个源代码文件。 当使用表达式’include("source.jl")`,文件’源的内容。jl`在发生`include’调用的模块的全局范围内计算。 如果’include("source.jl")'被多次调用’源。jl’文件计算的次数相同。 的包含路径’源。jl`相对于发生`include’调用的文件进行解释。 这使得移动源代码文件的子树变得容易。 在REPL中,包含路径相对于当前工作目录进行解释。 'pwd(’。
-
包下载,例如"导入X"或"使用X"。 导入机制允许您下载一个包-一个包含在模块中的独立的,可重用的Julia代码块-并在导入模块中以名称`X`提供此模块。 如果在Julia会话期间多次导入相同的包`X`,则只在第一次加载它-稍后,导入模块会收到指向它的链接。 但是,请记住,'import X’运算符可以在不同的上下文中加载不同的包:在主项目中,`X`可以引用一个名为`X`的包,在依赖项中可以引用其他也名为`X’的包。 有关更多信息,请参阅下文。
代码的包含非常简单:指定的源代码文件是在调用模块的上下文中计算的。 下载包是基于包含代码,但它服务 另一个目标。 本章的其余部分讨论了包下载的工作原理。
_pack_是一个具有标准结构的源代码树,提供可在其他Julia项目中使用的功能。 使用`import X`或`using X`运算符加载包。 它们使名为`X`的包在调用import语句的模块中可用。 表达式’import X’中’X`的含义取决于上下文:下载的包’X’取决于使用运算符的代码。 因此,表达式’import X`的处理分两个阶段进行:首先,确定在此上下文中哪个包是包`X`,然后此包`X`位于何处。
要回答这些问题,将在常量中列出的项目环境中执行搜索 'LOAD_PATH'用于项目文件('Project.toml’或’JuliaProject.toml`),清单文件('Manifest.toml’或’JuliaManifest.toml’或后缀为'-v的相同名称{major}.{minor}.某些版本的toml`)或带有源代码文件的文件夹。
包裹联合会
通常,包由其名称唯一标识。 但是,有时一个项目可能需要使用两个名称相同的不同包。 虽然您可以简单地重命名其中一个包来执行此操作,但这样的操作可能会在大型共享代码库中导致严重问题。 因此,Julia代码加载机制允许您使用相同的名称来引用应用程序的不同部分中的不同包。
Julia支持联合包管理。 这意味着不同的独立方可以维护自己的公共和私有软件包的注册管理机构,项目可以使用来自不同注册管理机构的公共和私有软件包的组合。 一组通用的工具和工作流用于安装和管理来自不同注册表的软件包。 Julia附带的’Pkg’包管理器允许您安装和管理项目依赖关系。 它允许您创建项目文件(描述项目所依赖的其他项目)和清单文件(其中包含项目的完整依赖关系图的版本),以及使用它们。
联邦的一个后果是缺乏集中的包命名系统。 不同的对象可以使用相同的名称来引用不相关的包。 这是不可避免的,因为对象不协调它们的行动,甚至可能不知道彼此的存在。 由于缺乏集中的命名系统,具有相同名称的不同包最终可能会在同一个项目中使用。 Julia中的包加载机制不要求包名称是全局唯一的,即使在单个项目的依赖关系图中也是如此。 相反,包由https://en.wikipedia.org/wiki/Universally_unique_identifier 创建包时分配给每个包的[通用唯一标识符](Uuid)。 通常不需要直接使用这些相当繁琐的128位标识符,因为Pkg接管了生成和跟踪它们的任务。 然而,多亏了UUID,我们可以得到一个明确的问题答案--"X"指的是什么?_
由于分散式命名的问题相当抽象,因此分析特定情况以了解它可能是有用的。 假设您正在开发一个名为`App`的应用程序,它使用两个包:`Pub`和`Priv'。 'Priv’是您自己创建的私有包,`Pub`是您使用但不控制的公共包。 当您创建’Priv’包时,没有公开可用的名为`Priv`的包。 然而,后来,有人创建了一个完全不同的`Priv’包,它被发布并流行起来。 而且,它已经开始在’Pub’包中使用。 因此,下次更新Pub以获取最新功能并修复错误时,应用程序最终将使用两个名为Priv的不同软件包,这只是更新的结果。 "App"应用程序直接依赖于您的私有包"Priv",通过"Pub"间接依赖于新的公共包"Priv"。 由于这些’Priv’包是不同的,但两者都是正确操作`App`所必需的,因此表达式`import Priv`应根据其应用的位置引用不同的`Priv`包:在`App`代码中或在`Pub`代码中。 为此,Julia中的包加载机制通过Uuid区分两个"Priv"包,并根据上下文(调用"import"语句的模块)选择适当的一个。 如何发生这种情况取决于环境,并在以下各节中描述。
星期三
环境定义了’import X`和’using X’运算符在不同代码上下文中的含义以及它们加载的文件。 Julia中有两种类型的环境。
-
*项目环境*是一个包含项目文件和可能是清单文件的目录。 它们形成了一个隐式环境。 项目文件定义项目的直接依赖项的名称和标识符。 清单文件(如果可用)描述完整的依赖关系图,包括所有直接和间接依赖关系、每个依赖关系的确切版本以及查找和下载正确版本所需的信息。
-
*包目录*包含一组包的源代码树的嵌套目录。 它形成了一个隐式环境。 如果’X’是包目录中的嵌套目录并且文件`X/src/X.jl`存在,则包`X`在包目录环境中可用,并且`X/src/X.jl’是通过其下载的源代码文件。
这些环境可以组合使用以创建多层环境:一组有序的项目环境和包目录,它们重叠以形成单个复合环境。 在这种情况下,将确定哪些包可用以及从哪里下载它们的优先级和可见性规则相结合。 例如,Julia下载路径形成多层环境。
每个环境都有自己的用途。
-
项目环境提供*再现性*。 通过将项目环境与项目源代码的其余部分一起添加到版本控制系统(如Git存储库),您可以重现项目及其所有依赖项的确切状态。 特别是,清单文件包含每个依赖项的确切版本,该依赖项由源代码树的加密哈希标识。 这允许Pkg管理器获得确切的版本,并为每个依赖项准确下载正确的代码。
-
当需要仔细监控的项目环境时,包目录提供*方便*。 如果您需要在某处放置一组包并直接使用它们,而无需为它们创建项目环境,则它们非常有用。
-
多层环境允许您*添加*工具到主环境。 您可以将开发环境放在堆栈的末尾,以便可以从REPL和脚本访问它们,但不能从包访问它们。
在最一般的层面上,每个环境都定义了三种模式:根、图形和路径。 在确定表达式’import X`的含义时,使用根图和图形识别包`X',并使用路径图确定其源代码的位置。 下面详细描述三种方案中的每一种方案的目的。
-
roots(roots)
'name::Symbol'⟶'uuid::UUID
环境根架构将包名称分配给环境使主项目可用的所有顶级依赖项的Uuid(即,可以加载到"Main"中)。 当Julia在主项目中看到表达式`import X`时,她将`X`标识为`roots[:X]'。
-
graph(图)
'context::UUID
⟶'name::Symbol'⟶'uuid::UUID`环境图是一个多级架构,它为上下文中的每个UUID分配一个名称映射到一个UUID。 它的工作方式与根模式相同,但在给定的上下文(`context')内。 当Julia在包代码中遇到带有UUID`context`的表达式`import X`时,她将`X`标识为`graph[context][:X]'。 特别是,这意味着表达式’import X’可以根据`上下文’引用不同的包。
-
路径(路径):`uuid::UUID`×
名称::符号
⟶路径::字符串
路径方案为每对Uuid和包名称分配具有此包入口点的源代码文件所在的位置。 在’import X’表达式中的`X’元素通过根模式或图形解析为UUID(取决于下载是来自主项目还是来自依赖项)后,Julia通过在环境中搜索`paths[uuid,:X]`来确定下载哪个文件以 包含此文件时,会检测到名为`X`的模块。 下载包后,解析为相同uuid的所有后续导入操作将创建到已下载的包模块的绑定。
对于每种类型的环境,这三种方案的定义不同,如下面各节所述。
为了清楚起见,本章中的示例为根图、图和路径图提供了完整的数据结构。 但是,Julia包下载代码不会显式创建它们。 相反,它"懒惰地"只计算每个结构中足以加载给定包的那部分。 |
项目环境
项目环境由一个目录组成,其中包含一个名为"Project"的项目文件。toml’和一个名为`Manifest的可选清单文件。汤姆。 这些文件也可以被命名为’JuliaProject。toml’和’JuliaManifest.toml';在这种情况下,文件的项目。汤姆和清单。toml’被忽略。 这允许您同时使用依赖于名为"Project"的文件的工具。汤姆和清单。汤姆。 但是,对于仅在Julia上的项目,名称的项目。汤姆和清单。toml’是优选的。 但是,从Julia版本1.11开始,'(Julia)Manifest-v{major}.{minor}.toml'被识别为一种格式,其中特定的清单文件应该在某个版本的Julia中使用,即在同一个文件夹`Manifest-v1.11中。toml’将由版本1.11和`Manifest使用。toml'-由朱莉娅的任何其他版本。
项目环境的根、图和路径的方案定义如下。
环境的*root*架构由项目文件的内容确定,特别是其顶级`name`和`uuid`元素以及`[deps]`部分(所有这些都是可选的)。 考虑上面描述的虚构应用程序的项目文件的以下示例。
name = "App"
uuid = "8f986787-14fe-4607-ba5d-fbff2944afa9"
[deps]
Priv = "ba13f791-ae1d-465a-978b-69c3ad90f72b"
Pub = "c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"
此项目文件假定以下根方案,以Julia字典的形式呈现。
roots = Dict(
:App => UUID("8f986787-14fe-4607-ba5d-fbff2944afa9"),
:Priv => UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b"),
:Pub => UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"),
)
使用此根方案,App’代码中的’import Priv’运算符指示Julia搜索’roots[:Priv]
,这将导致值`ba13f791-ae1d-465a-978b-69c3ad90f72b`-在此上下文中加载`Priv`包的UUID。 UUID定义在主应用程序中计算`import Priv`语句时应该下载和使用哪个包’Priv'。
项目环境的依赖关系图由清单文件的内容(如果有)确定。 如果没有清单文件,则图表为空。 清单文件对项目的每个直接或间接依赖项都有一个单独的部分。 对于每个依赖项,包的UUID和源代码树的散列或源代码的显式路径都在文件中指定。 考虑`App`应用程序的清单文件的以下示例:
[[Priv]] # частный
deps = ["Pub", "Zebra"]
uuid = "ba13f791-ae1d-465a-978b-69c3ad90f72b"
path = "deps/Priv"
[[Priv]] # общедоступный
uuid = "2d15fe94-a1f7-436c-a4d8-07a9a496e01c"
git-tree-sha1 = "1bf63d3be994fe83456a03b874b409cfd59a6373"
version = "0.1.5"
[[Pub]]
uuid = "c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"
git-tree-sha1 = "9ebd50e2b0dd1e110e842df3b433cb5869b0dd38"
version = "2.1.4"
[Pub.deps]
Priv = "2d15fe94-a1f7-436c-a4d8-07a9a496e01c"
Zebra = "f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"
[[Zebra]]
uuid = "f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"
git-tree-sha1 = "e808e36a5d7173974b90a15a353b564f3494092f"
version = "3.4.2"
这个清单文件完全描述了"App"项目可能的依赖关系图。
-
该应用程序使用两个名为"Priv"的不同包:私有包(根依赖项)和公共包(通过"Pub"间接依赖项)。 它们在其独特的Uuid上有所不同,并且具有不同的依赖关系。 私人套餐"Priv"取决于套餐"Pub"和"Zebra"。 公共包’Priv’没有依赖关系。
-
该应用程序还依赖于`Pub’包,这反过来又依赖于公共’Priv’包和私有`Priv’包所依赖的相同`Zebra`包。
表示为字典的依赖关系图如下所示:
graph = Dict(
# Частный пакет Priv:
UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b") => Dict(
:Pub => UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"),
:Zebra => UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"),
),
# Общедоступный пакет Priv:
UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c") => Dict(),
# Pub:
UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1") => Dict(
:Priv => UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c"),
:Zebra => UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"),
),
# Zebra:
UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62") => Dict(),
)
使用此依赖关系图,当Julia在带有UUID`c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1`的`Pub`包中遇到表达式`import Priv`时,将执行以下搜索:
graph[UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1")][:Priv]
结果,返回值`2d15fe94-a1f7-436c-a4d8-07a9a496e01c`,这意味着在`Pub`包的上下文中,表达式`import Priv`引用公共包`Priv`,而不是应用程序直接依赖的私有包。 这就是主项目中的名称`Priv`如何用于引用此名称在依赖项中指示的包以外的包,这使得在包系统中可以使用相同的名称。
在`应用程序`的主代码中计算表达式`import Zebra’时会发生什么? 由于Zebra软件包未在项目文件中列出,因此导入将失败,尽管Zebra位于清单文件中。 此外,如果在uuid`2d15fe94-a1f7-436c-a4d8-07a9a496e01c`的公共软件包`Priv`中使用`import Zebra`语句,它也会失败,因为此软件包`Priv’没有在清单文件中声明依赖项,因此无法下载其他软件包。 Zebra包只能由清单文件中明确指定为依赖项的包加载,即Pub包和Priv包之一。
项目环境的*路径图*是从清单文件中提取的。 具有标识符’uuid`和名称`X`的包的路径根据以下规则(按指定顺序)确定。
-
如果目录中的项目文件与标识符`uuid`和名称`X’匹配,则会发生以下情况。 如果它有一个顶级的’path’元素,那么’uuid’被映射到这个路径,这个路径是相对于包含项目文件的目录来解释的。 否则,`uuid’将映射到相对于包含项目文件的目录的路径`src/X.jl'。
-
如果不满足上述条件,但存在包含对应于`uuid`标识符的节的项目文件的清单文件,则发生以下情况。 如果它有一个`path’元素,则使用此路径(相对于包含清单文件的目录)。 如果它包含`git-tree-sha1’元素,则为`uuid`和`git-tree-sha1`值计算确定性哈希函数,并考虑其结果(让我们将此值称为`slug`),并且在全局Julia DEPOT_PATH数组中的每个目录中搜索`packages`目/X/$slug`。 将使用第一个这样找到的目录。
如果满足这些条件中的任何一个,则源代码入口点的路径将是获得的结果,或者是添加`src/X.jl`的相对于此结果的路径。 否则,不存在与`uuid`标识符对应的路径。 如果在下载软件包’X`时找不到源代码的路径,则搜索失败,可能会提示用户安装相应版本的软件包或以另一种方式修复情况(例如,将`X`声明为依赖项)。
在上面的清单文件示例中,第一个`Priv`包的路径(使用uuid`ba13f791-ae1d-465a-978b-69c3ad90f72b`)定义如下:Julia在清单文件中搜索此包的部分,找到`path`元素,搜索相对于`App’项目目录的路径`deps/Priv`(假设`App`代码位于路径`/home/me/projects/app`),找到目录`/home/me/Projects/app/deps/PRIV`并因此从中下载`priv`包。
如果需要下载另一个包`Priv`(带有UUID`2d15fe94-a1f7-436c-a4d8-07a9a496e01c`),Julia环境会在清单中找到它的部分,但是,它不包含`path`元素,而是包含`git-tree-sha1’元素。 因此,将计算这对Uuid和SHA-1哈希的slug值,并获得HDkrT结果(如何执行此计算并不重要,但函数是一致和确定性的)。 这意味着’Priv’包的路径将是路径`packages/Priv/HDkrT/src/Priv'。jl`中的一个包存储库中。 假设’DEPOT_PATH’变量包含值'["/home/me/。julia","/usr/local/julia"]'。 在这种情况下,Julia会检查以下路径是否存在:
-
'/家/我/。朱莉娅/包/Priv/HDkrT`
-
'/usr/local/julia/packages/Priv/HDkrT`
然后Julia尝试从文件`packages/Priv/HDKrT/src/Priv`下载公共软件包`Priv'。在找到这些路径中的第一个的存储库中jl`。
下面是"App"项目环境示例的可能路径图,在搜索本地文件系统后,它在上面的依赖关系图的清单中显示。
paths = Dict(
# Частный пакет Priv:
(UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b"), :Priv) =>
# относительная точка входа внутри репозитория `App`:
"/home/me/projects/App/deps/Priv/src/Priv.jl",
# Общедоступный пакет Priv:
(UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c"), :Priv) =>
# пакет, установленный в системном хранилище:
"/usr/local/julia/packages/Priv/HDkr/src/Priv.jl",
# Pub:
(UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"), :Pub) =>
# пакет, установленный в пользовательском хранилище:
"/home/me/.julia/packages/Pub/oKpw/src/Pub.jl",
# Zebra:
(UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"), :Zebra) =>
# пакет, установленный в системном хранилище:
"/usr/local/julia/packages/Zebra/me9k/src/Zebra.jl",
)
在示例图中,有三种不同类型的包位置(第一个位置和第三个位置是默认下载路径的一部分)。
-
"https://stackoverflow.com/a/35109534 "私有包"Priv的[专有副本]托管在"应用程序"存储库中。
-
公共软件包"Priv"和"Zebra"位于系统存储中,软件包由系统管理员安装和管理。 它们可供系统的所有用户使用。
-
'Pub’软件包位于用户存储中,其中软件包由活动用户安装。 它们仅对安装它们的用户可用。
包装目录
包目录是一种更简单的环境类型,不允许解决名称冲突。 在包目录中,一组顶级包是一组"看起来"像包的嵌套目录。 包’X’存在于包目录中,如果以下入口点文件之一存在于该目录中:
-
X.jl
-
X/src/X.jl
-
X.jl/src/X.jl
包可以在包目录中导入哪些依赖项取决于包中是否存在项目文件。
-
如果有项目文件,则只能导入其部分"[deps]"中指定的包。
-
如果没有项目文件,则可以导入任何顶级包,即可以在主模块或REPL中加载的相同包。
*根架构*通过分析项目目录的内容并创建所有可用包的列表来确定。 此外,在"X"文件夹中找到的每个包都被分配一个UUID,如下所示。
-
如果有一个’X/项目。toml’文件并且它包含一个’uuid’元素,那么这个’uuid’元素将是标识符值。
-
如果文件’X/项目。toml’存在,但其中没有顶级UUID元素,虚拟标识符’uuid`是通过将规范(真实)路径散列到’X/Project来生成的。汤姆。
-
否则(如果没有’项目。toml’文件),`uuid’标识符将由https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID [只是零]。
包目录的依赖关系图由每个包子目录中项目文件的存在和内容决定。 以下规则适用。
-
如果包的子目录中没有项目文件,则该包不包含在图表中,并且其代码中的导入运算符在主项目和REPL中都被视为顶级运算符。
-
如果包的附加目录中有一个项目文件,则该模式将用作项目文件中其UUID`[deps]`的图形元素。 如果缺少此部分,则该元素将被视为空。
假设包目录具有以下结构和内容:
Aardvark/ src/Aardvark.jl: import Bobcat import Cobra Bobcat/ Project.toml: [deps] Cobra = "4725e24d-f727-424b-bca0-c4307a3456fa" Dingo = "7a7925be-828c-4418-bbeb-bac8dfc843bc" src/Bobcat.jl: import Cobra import Dingo Cobra/ Project.toml: uuid = "4725e24d-f727-424b-bca0-c4307a3456fa" [deps] Dingo = "7a7925be-828c-4418-bbeb-bac8dfc843bc" src/Cobra.jl: import Dingo 野狗/ 工程。汤姆尔: uuid="7a7925be-828c-4418-bbeb-bac8dfc843bc" src/野狗.jl: #没有进口
以字典的形式,对应的根结构将呈现如下:
roots = Dict(
:Aardvark => UUID("00000000-0000-0000-0000-000000000000"), # файла проекта нет, нулевой UUID
:Bobcat => UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf"), # фиктивный UUID на основе пути
:Cobra => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), # UUID из файла проекта
:Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), # UUID из файла проекта
)
以字典的形式,相应的图结构将表示如下:
graph = Dict(
# Bobcat:
UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf") => Dict(
:Cobra => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"),
:Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"),
),
# Cobra:
UUID("4725e24d-f727-424b-bca0-c4307a3456fa") => Dict(
:Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"),
),
# Dingo:
UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc") => Dict(),
)
注意一些重要的规则。
-
没有项目文件的包可以依赖于任何顶级包,并且由于包目录中的所有包都在顶级可用,因此它可以导入环境中的所有包。
-
具有项目文件的包不能依赖于没有这样的文件的包,因为具有项目文件的包只能加载图形架构中可用的包,并且其中没有没有项目文件的包。
-
只有没有项目文件的包可以依赖于具有项目文件的包,但没有明确指定的UUID,因为分配给这些包的虚拟Uuid仅用于内部使用。
让我们看看这些规则在我们的例子中是如何应用的。
-
'Aardvark’可以导入任何’山猫`,'眼镜蛇`或’野狗’包;事实上,`山猫`和`眼镜蛇’是进口的。
-
"山猫"可以而且确实导入软件包"Cobra"和"Dingo",它们没有带有Uuid的项目文件,并且在"山猫"软件包的"[deps]"中声明为依赖项。
-
"山猫"可能依赖于"土豚",因为"土豚"包没有项目文件。
-
'Cobra’可以并且确实导入’Dingo`包,它有一个项目文件和一个UUID,并在`Cobra’包的`[deps]'中声明为依赖项。
-
'Cobra’不能依赖’Aardvark’或`Bobcat’包,因为它们没有真正的Uuid。
-
'Dingo’包不能导入任何东西,因为它有一个项目文件,但没有一个部分`[deps]`。
*包目录中的路径方案*非常简单:在其中,嵌套目录的名称映射到入口点的路径。 换句话说,如果我们示例中的项目目录路径看起来像'/home/me/animals`,那么’paths’模式可以用这样的字典来表示:
paths = Dict(
(UUID("00000000-0000-0000-0000-000000000000"), :Aardvark) =>
"/home/me/AnimalPackages/Aardvark/src/Aardvark.jl",
(UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf"), :Bobcat) =>
"/home/me/AnimalPackages/Bobcat/src/Bobcat.jl",
(UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), :Cobra) =>
"/home/me/AnimalPackages/Cobra/src/Cobra.jl",
(UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), :Dingo) =>
"/home/me/AnimalPackages/Dingo/src/Dingo.jl",
)
由于根据定义,包目录环境中的所有包都是具有相应入口点文件的嵌套目录,因此其"路径"模式的元素始终具有这种形式。
媒体堆栈
第三种也是最后一种类型的环境是其中其他类型的环境彼此重叠的环境,形成其中每个组件环境的包可用的单个环境。 这种复合介质称为介质堆栈。 Julia全局变量’LOAD_PATH’定义了运行Julia进程的环境堆栈。 为了让Julia进程只能从一个项目或包目录访问包,不要在`LOAD_PATH`中指定其他环境。 但是,访问一些工具通常很有用-标准库,分析器,调试器,辅助程序等。 -即使它们不是当前项目的依赖项。 如果您将具有这些工具的环境添加到下载路径,它们将立即在顶级代码中可用,因此您不需要将它们添加到项目中。
属于媒体堆栈的组件的根,图和路径的数据结构非常简单地组合为字典,并且在键匹配的情况下,优先级被赋予较早的条目。 换句话说,如果有一个堆栈’stack=[env₁,env₂,。..]`,则发生以下情况。
roots = reduce(merge, reverse([roots₁, roots₂, …]))
graph = reduce(merge, reverse([graph₁, graph₂, …]))
paths = reduce(merge, reverse([paths₁, paths₂, …]))
带有下标的变量’rootsᵢ`,'graphᵢ’和’pathsᵢ’对应于带有下标包含在`堆栈`中的环境`envᵢ`。 使用’reverse’函数的原因是,当键在作为参数传递给`merge`函数的字典中匹配时,优先考虑最后一个参数而不是第一个参数。 这种方法有几个值得注意的特点。
-
主环境,即堆栈中的第一个环境,完全集成到多层环境中。 堆栈中第一个环境的依赖关系图保证完全和不变地包含在分层环境中,包括所有依赖关系的相同版本。
-
来自非核心环境的软件包最终可能会使用不兼容的依赖版本,即使它们自己的环境完全兼容。 如果其中一个依赖项被堆栈中较早环境的版本替换(根据graph,path或两者),则会发生这种情况。
由于主要环境通常是当前项目的环境,并且堆栈中的后续环境包含额外的工具,因此这种折衷是合理的:开发工具停止工作比整个项目更好。 通常,如果发生这种不兼容性,则有必要将开发工具更新为与主项目兼容的版本。
软件包扩展
包扩展是在当前Julia会话中加载某组其他包(其"触发器")时自动加载的模块。 扩展在`[扩展]"部分的项目文件中定义。 扩展触发器是项目文件的`[weakdeps]部分(可能,但很少,在
[deps]`)中列出的其他包的子集。 这些包可能有compat条目,就像其他包一样。
name = "MyPackage"
[compat]
ExtDep = "1.0"
OtherExtDep = "1.0"
[weakdeps]
ExtDep = "c9a23..." # uuid
OtherExtDep = "862e..." # uuid
[extensions]
BarExt = ["ExtDep", "OtherExtDep"]
FooExt = "ExtDep"
...
"扩展"部分中的键是扩展名的名称。 当右侧列出的所有软件包(触发器)都被加载时,它们被加载。 如果扩展只有一个触发器,则为了简洁起见,触发器列表可以写成字符串。 `FooExt`扩展的入口点在`ext/FooExt。jl’或’ext/FooExt/FooExt。jl'。 扩展的内容通常具有这种结构:
module FooExt # Загрузка главного пакета и триггеров using MyPackage, ExtDep # Расширение функциональности главного пакета посредством типов из триггеров MyPackage.func(x::ExtDep.SomeStruct) = ... 结束
将扩展包添加到环境中时,"weakdeps"和"extensions"部分将保存在该包部分的清单文件中。 包的依赖关系搜索规则与其"父"相同,只是列表中的触发器也被视为依赖关系。
包和环境首选项
首选项是影响环境中包行为的元数据字典。 首选项系统支持在编译时读取首选项。 这意味着在下载代码之前,您需要确保Julia选择的预编译文件是根据当前环境的首选项构建的。 用于更改首选项的公共API包含在包中https://github.com/JuliaPackaging/Preferences.jl [首选项。jl]。 首选项作为TOML字典存储在'(Julia)LocalPreferences中。当前活动项目旁边的toml`文件。 导出时,首选项被转移到文件'(Julia)项目。汤姆。 这个想法是共享项目可以有共同的首选项,但用户可以在LocalPreferences中使用自己的设置复盖它们。toml文件,顾名思义,应该添加到文件中。吉蒂尼诺尔。
编译期间访问的首选项会自动标记为编译时首选项。 对它们的任何更改都会强制Julia编译器重新编译所有缓存的预编译文件('。ji’和相应的文件'.so`’。dll’或`。dylib`)用于该模块。 为此,在编译期间,序列化所有编译时首选项的散列,然后在搜索必要的文件时,检查是否符合当前环境。
可以在存储级别为首选项设置默认值;如果Foo包安装在全局环境中并具有指定的首选项,则只要全局环境包含在`LOAD_PATH`中,就会应用它们。 与环境堆栈中较高的环境相关的首选项由下载路径中较低级别的条目复盖,直到当前活动项目。 这允许默认首选项存在于项目级别,并且在为活动项目继承时,它们可以组合甚至完全复盖。 有关如何允许或禁用组合首选项的更多信息,请参阅"首选项"的文档字符串。set_preferences!()'功能。