AnyMath 文档

代码加载

注意本章涵盖了包加载的技术细节。 要安装软件包,请使用 Pkg,Pkg,Julia的内置软件包管理器,用于将软件包添加到您的活动环境中。 要使用活动环境中已有的包,请写 进口X使用X,如所述 模块文档

定义

Julia有两种加载代码的机制:

  1. *代码包含:*例如 include("源。jl"). 包含允许您跨多个源文件拆分单个程序。 表达式 include("源。jl") 导致文件的内容 来源。jl 在模块的全局范围内进行评估, 包括 呼叫发生。 如果 include("源。jl") 被调用多次, 来源。jl 被评价多次。 包含的路径, 来源。jl,是相对于文件的解释 包括 呼叫发生。 这使得重新定位源文件的子树变得简单。 在REPL中,包含的路径相对于当前工作目录进行解释, 残疾人士().

  2. *包装装载:*例如 进口X使用X. 导入机制允许您加载一个包-即一个独立的,可重用的Julia代码集合,包装在一个模块中-并使结果模块按名称可用 X 导入模块的内部。 如果相同 X 包在同一个Julia会话中被导入多次,它只在第一次被加载—​在后续的导入中,导入模块得到对同一个模块的引用。 但请注意, 进口X 可以在不同的上下文中加载不同的包: X 可以引用一个名为 X 在主项目中,但可能是不同的包也命名 X 在每个依赖项中。 更多关于这个下面。

代码包含非常简单明了:它在调用者的上下文中评估给定的源文件。 包加载是建立在代码包含之上的,并提供一个 不同的目的。 本章的其余部分侧重于包加载的行为和机制。

_package_是一个源代码树,具有标准布局,提供可被其他Julia项目重用的功能。 一个包由 进口X使用X 发言。 这些语句还使模块命名 X--加载包代码的结果-在发生import语句的模块中可用。 的意义 X进口X 是上下文相关的:哪个 X 包的加载取决于语句发生在什么代码中。 因此,处理 进口X 发生在两个阶段:首先,它确定*什么*包被定义为 X 在这种情况下;第二,它确定*在哪里*该特定 X 包找到。

通过搜索列于 LOAD_PATH对于项目文件(工程。汤姆尔朱利亚项目。汤姆尔),清单文件(清单。汤姆尔JuliaManifest.汤姆尔,或以相同名称为后缀的 -v{major}.{minor}.汤姆 对于特定版本),或源文件的文件夹。

包裹联合会

大多数情况下,一个包是唯一可识别的,只需从它的名字。 但是,有时项目可能会遇到需要使用两个共享相同名称的不同包的情况。 虽然您可以通过重命名其中一个包来解决这个问题,但在大型共享代码库中被迫这样做可能会造成很大的破坏性。 相反,Julia的代码加载机制允许相同的包名引用应用程序不同组件中的不同包。

Julia支持联合包管理,这意味着多个独立方可以维护公共包和私有包以及包的注册表,并且项目可以依赖来自不同注册表的公共包和私有包的混合。 使用一组通用的工具和工作流安装和管理来自各种注册表的软件包。 该 Pkg,Pkg Julia附带的包管理器允许您安装和管理项目的依赖项。 它帮助创建和操作项目文件(描述项目依赖的其他项目)和清单文件(快照项目完整依赖关系图的精确版本)。

联合的一个后果是不能有包命名的中央权威。 不同的实体可以使用相同的名称来引用不相关的包。 这种可能性是不可避免的,因为这些实体不协调,甚至可能不知道彼此。 由于缺乏中央命名机构,单个项目最终可能取决于具有相同名称的不同包。 Julia的包加载机制不要求包名是全局唯一的,即使在单个项目的依赖关系图中也是如此。 相反,包由https://en.wikipedia.org/wiki/Universally_unique_identifier[通用唯一标识符](uuid),在创建每个包时分配。 通常,您不必直接使用这些有点繁琐的128位标识符,因为 Pkg,Pkg 将负责为您生成和跟踪它们。 但是,这些Uuid为_"包做什么"的问题提供了明确的答案 X 指?"_

由于分散式命名问题有些抽象,因此通过一个具体的场景来理解这个问题可能会有所帮助。 假设您正在开发一个名为 应用程序,其中使用两个包: 酒吧普里夫. 普里夫 是您创建的私有包,而 酒吧 是您使用但不控制的公共包。 当你创建 普里夫,名称没有公共包 普里夫. 然而,随后,一个不相关的包也被命名为 普里夫 已经出版并流行起来。 事实上, 酒吧 包已经开始使用它。 因此,当您下次升级时 酒吧 获取最新的错误修复和功能, 应用程序 最终将取决于两个不同的包命名 普里夫--除了升级外,你没有任何行动。 应用程序 直接依赖于你的私人 普里夫 包,和一个间接依赖,通过 酒吧,关于新公 普里夫 包裹。 由于这两个 普里夫 包是不同的,但都是必需的 应用程序 要继续正常工作,表达式 进口Priv 必须指不同的 普里夫 包取决于它是否发生在 应用程序的代码或在 酒吧的代码。 为了处理这一点,Julia的包加载机制将两者区分开来 普里夫 通过其UUID打包并根据其上下文(调用的模块)选择正确的 进口). 这种区分的工作方式由环境决定,如下面各节所述。

环境

环境决定了什么 进口X使用X mean在各种代码上下文中以及这些语句导致加载的文件。 Julia了解两种环境:

  1. *项目环境*是包含项目文件和可选清单文件的目录,并形成_explicit environment_。 项目文件确定项目的直接依赖项的名称和标识。 清单文件,如果存在,给出一个完整的依赖关系图,包括所有直接和间接依赖关系,每个依赖关系的确切版本,以及足够的信息来定位和加载正确的版本。

  2. *包目录*是包含一组包的源树作为子目录的目录,并形成_implicit environment_。 如果 X 是包目录的子目录, X/src/X.jl 存在,则包 X 可在包目录环境和 X/src/X.jl 是加载它的源文件。

它们可以混合在一起创建*堆叠环境*:一组有序的项目环境和包目录,叠加起来形成一个单一的复合环境。 然后,优先级和可见性规则结合起来确定哪些包可用以及它们从哪里加载。 例如,Julia的加载路径形成了一个堆叠环境。

这些环境各有不同的用途:

*项目环境提供*可重复性*。 通过将项目环境与项目源代码的其余部分一起检查到版本控制(例如git存储库)中,您可以重现项目及其所有依赖项的确切状态。 特别是,清单文件捕获了每个依赖项的确切版本,由其源树的加密哈希标识,这使得 Pkg,Pkg 要检索正确的版本,并确保您正在运行为所有依赖项记录的确切代码。 *包目录提供*方便*当一个完整的仔细跟踪的项目环境是不必要的。 当您希望将一组包放在某个地方并能够直接使用它们时,它们非常有用,而无需为它们创建项目环境。 *堆叠环境允许*添加*工具到主环境。 您可以将开发工具的环境推送到堆栈的末尾,以使它们可以从REPL和脚本中使用,但不能从包内部使用。

注意当从堆栈中除活动环境之外的其他环境加载包时,包将在活动环境的上下文中加载。 这意味着软件包将像在活动环境中导入一样加载,这可能会影响其依赖项版本的解析方式。 当预编译这样的包时,它将被标记为 (连载) 预编译作业,这意味着它的依赖项将在同一作业内进行串联预编译,这可能会更慢。

在高级别上,每个环境在概念上定义了三个映射:根,图和路径。 当解决 进口X,根和图形映射用于确定 X,而路径映射用于定位 X. 三张地图的具体作用是:

**根:* 名称::符号掳忙篓霉::UUID 环境的根映射为Uuid分配了环境为主项目提供的所有顶级依赖项(即可以加载的依赖项)的包名 主要). 当朱莉娅遇到 进口X 在主项目中,它查找 X 作为 根[:X]. **图表:* 上下文::UUID名称::符号掳忙篓霉::UUID 环境的图表是一个多层次的地图,它为每个 上下文环境 UUID,从名称到Uuid的映射,类似于根映射,但特定于该映射 上下文环境. 当朱莉娅看到 进口X 在uuid为的包的代码中 上下文环境,它查找的身份 X 作为 图[上下文][:X]. 特别是,这意味着 进口X 可以参考不同的包取决于 上下文环境. **路径:* 掳忙篓霉::UUID × 名称::符号路径::字符串 路径映射分配给每个包UUID-name对,即该包的入口点源文件的位置。 后的身份 X进口X 已经通过roots或graph解析为UUID(取决于它是从主项目还是依赖项加载),Julia决定加载要获取的文件 X 通过向上看 路径[uuid,:X] 环境中。 包括这个文件应该定义一个名为 X. 一旦这个包被加载,任何后续的导入解析到相同的 uuid 将创建一个新的绑定到已经加载的包模块。

每种环境对这三个映射的定义都不同,如下面各节所述。

注意为了便于理解,本章的示例显示了根、图和路径的完整数据结构。 但是,Julia的包加载代码并没有明确地创建这些。 相反,它只懒地计算每个结构的多少,因为它需要加载一个给定的包。

项目环境

项目环境由包含名为"项目文件"的目录确定 工程。汤姆尔,并可选地称为清单文件 清单。汤姆尔. 这些文件也可以被称为 朱利亚项目。汤姆尔JuliaManifest.汤姆尔,在这种情况下 工程。汤姆尔清单。汤姆尔 被忽略。 这允许与其他可能考虑称为文件的工具共存 工程。汤姆尔清单。汤姆尔 显着。 然而,对于纯Julia项目,名称 工程。汤姆尔清单。汤姆尔 是优选的。 但是,从Julia v1.10.8开始, (朱莉娅)Manifest-v{major}.{minor}.汤姆 被识别为一种格式,使给定的julia版本使用特定的清单文件,即在同一个文件夹中,一个 清单-v1.11。汤姆尔 将被v1.11和 清单。汤姆尔 通过任何其他julia版本。

项目环境的根、图形和路径映射定义如下:

*环境的根映射*由项目文件的内容决定,特别是它的顶层 姓名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"),
)

给定这个根图,在 应用程序语句的代码 进口Priv 会导致茱莉亚抬头 根[:Priv],其产 ba13f791-ae1d-465a-978b-69c3ad90f72b,的UUID 普里夫 要在该上下文中加载的包。 此UUID标识哪些 普里夫 包在主应用程序评估时加载和使用 进口Priv.

*项目环境的依赖关系图*由清单文件的内容(如果存在)确定。 如果没有清单文件,则graph为空。 清单文件包含每个项目的直接或间接依赖项的节。 对于每个依赖项,该文件列出了包的UUID和源代码树哈希或源代码的显式路径。 考虑以下示例清单文件 应用程序:

[[Priv]] # the private one
deps = ["Pub", "Zebra"]
uuid = "ba13f791-ae1d-465a-978b-69c3ad90f72b"
path = "deps/Priv"

[[Priv]] # the public one
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"

此清单文件描述了一个可能的完整依赖关系图 应用程序 工程项目:

*有两个不同的包命名 普里夫 应用程序使用的。 它使用一个私有包,这是一个根依赖项,以及一个公共包,这是一个间接依赖项。 酒吧. 这些由他们不同的Uuid区别,并且他们有不同的deps: **私人 普里夫 取决于 酒吧斑马 包裹。 **公众 普里夫 没有依赖关系。 *应用程序还取决于 酒吧 包,这反过来又取决于公众 普里夫 和一样 斑马 包装私人的 普里夫 包取决于。

此依赖关系图表示为字典,如下所示:

graph = Dict(
    # Priv – the private one:
    UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b") => Dict(
        :Pub   => UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"),
        :Zebra => UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"),
    ),
    # Priv – the public one:
    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(),
)

鉴于这种依赖 图表,当朱莉娅看到 进口Priv酒吧 包—​其中有UUID c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1--它抬头:

graph[UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1")][:Priv]

并得到 2d15fe94-a1f7-436c-a4d8-07a9a496e01c,这表明在上下文中 酒吧 包裹, 进口Priv 指公众 普里夫 包,而不是应用程序直接依赖的私有包。 名字就是这样 普里夫 可以在主项目中引用不同的包,而不是在它的一个包的依赖项中引用不同的包,这允许在包生态系统中重复名称。

如果发生什么 进口斑马 在主要的评估 应用程序 代码库? 自 斑马 没有出现在项目文件中,即使导入失败 斑马 _does_出现在清单文件中。 此外,如果 进口斑马 发生在公众 普里夫 包—​有UUID的那个 2d15fe94-a1f7-436c-a4d8-07a9a496e01c--那也就失败了 普里夫 包在清单文件中没有声明的依赖项,因此无法加载任何包。 该 斑马 包只能由在清单文件中显示为显式依赖项的包加载: 酒吧 包和其中一个 普里夫 包裹。

*项目环境的路径映射*是从清单文件中提取的。 包的路径 uuid 命名 X 由这些规则(按顺序)决定:

  1. 如果目录中的项目文件匹配 uuid 及姓名 X,那么要么: **它有一个顶层 入口文件 入,则 uuid 将映射到该路径,相对于包含项目文件的目录进行解释。

*否则, uuid 被映射到 src/X.jl 相对于包含项目文件的目录。

    1. 如果不是上述情况,并且项目文件具有相应的清单文件,并且清单包含匹配的节 uuid 然后: ***如果它有一个 路径 条目,使用该路径(相对于包含清单文件的目录)。

       **如果它有一个 `git-树-sha1` 条目,计算一个确定性哈希函数 `uuid` 和 `git-树-sha1`--叫它 `鼻涕虫`--并寻找一个名为目录 `包装/X/$slug` 在Julia中的每个目录中 `DEPOT_PATH` 全局数组。 使用存在的第一个这样的目录。
      . 如果这是一个目录,那么 `uuid` 被映射到 `src/X.jl` 除非匹配的清单节具有 `入口文件` 在这种情况下使用的条目。 在这两种情况下,这些都是相对于2.1中的目录。

如果其中任何一个导致成功,则源代码入口点的路径将是该结果,该结果的相对路径加上 src/X.jl;否则,没有路径映射为 uuid. 装货时 X,如果没有找到源代码路径,查找将失败,并且可能会提示用户安装适当的软件包版本或采取其他纠正措施(例如声明 X 作为依赖)。

在上面的示例清单文件中,要找到第一个清单文件的路径 普里夫 包—​有UUID的那个 ba13f791-ae1d-465a-978b-69c3ad90f72b--Julia在清单文件中查找它的节,看到它有一个 路径 进入,看 副警长/Priv 相对于 应用程序 项目目录—​让我们假设 应用程序 代码生活在 /home/me/项目/应用--看到了 /home/me/项目/应用/deps/Priv 存在,因此负载 普里夫 从那里开始。

另一方面,如果朱莉娅正在加载另一个_ 普里夫 包—​有UUID的那个 2d15fe94-a1f7-436c-a4d8-07a9a496e01c--它在清单中找到它的节,看到它没有_not_有一个 路径 条目,但它确实有一个 git-树-sha1 进入。 然后计算 鼻涕虫 对于这个UUID/SHA-1对,这是 HDkrT的 (这个计算的确切细节并不重要,但它是一致的和确定性的)。 这意味着这条路 普里夫 包装将是 包/Priv/HDkrT/src/Priv。jl 在其中一个包装库中。 假设的内容 DEPOT_PATH["/家/我/。julia","/usr/local/julia"],然后Julia会查看以下路径,看看它们是否存在:

  1. /家/我/。朱莉娅/包/Priv/HDkrT

  2. usr/本地/朱莉娅/包装/Priv/HDkrT

Julia使用存在的第一个来尝试加载公共 普里夫 从文件打包 包/Priv/HDKrT/src/Priv。jl 在发现它的仓库里。

下面是我们示例的可能路径映射的表示 应用程序 项目环境,如上面给出的依赖关系图清单中所提供的,在搜索本地文件系统后:

paths = Dict(
    # Priv – the private one:
    (UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b"), :Priv) =>
        # relative entry-point inside `App` repo:
        "/home/me/projects/App/deps/Priv/src/Priv.jl",
    # Priv – the public one:
    (UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c"), :Priv) =>
        # package installed in the system depot:
        "/usr/local/julia/packages/Priv/HDkr/src/Priv.jl",
    # Pub:
    (UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"), :Pub) =>
        # package installed in the user depot:
        "/home/me/.julia/packages/Pub/oKpw/src/Pub.jl",
    # Zebra:
    (UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"), :Zebra) =>
        # package installed in the system depot:
        "/usr/local/julia/packages/Zebra/me9k/src/Zebra.jl",
)

此示例地图包括三种不同类型的包位置(第一个和第三个是默认加载路径的一部分):

  1. 私人 普里夫 包是"世仇"里面 应用程序 存储库。

  2. 公众 普里夫斑马 软件包位于系统库中,系统管理员在其中安装和管理软件包。 这些可供系统上的所有用户使用。

  3. 酒吧 包在用户仓库中,用户安装的包在那里。 这些仅对安装它们的用户可用。

包目录

包目录提供了一种更简单的环境,而无法处理名称冲突。 在包目录中,顶级包集是"看起来像"包的子目录集。 一个包裹 X 如果目录包含以下"入口点"文件之一,则存在于包目录中:

* X.jl * X/src/X.jl * X.jl/src/X.jl

包目录中的包可以导入哪些依赖项取决于包是否包含项目文件:

*如果它有一个项目文件,它只能导入那些在 [deps] 节的项目文件。 *如果它没有项目文件,它可以导入任何顶级包-即可以加载的相同包 主要 或者REPL。

*根映射*是通过检查包目录的内容来确定的,以生成存在的所有包的列表。 此外,将为每个条目分配一个UUID,如下所示:对于在文件夹中找到的给定包 X…​

  1. 如果 X/项目。汤姆尔 存在并具有 uuid 入,则 uuid 是那个值。

  2. 如果 X/项目。汤姆尔 存在,但_not_是否有顶级UUID条目, uuid 是通过散列规范(真实)路径生成的虚拟UUID X/项目。汤姆尔.

  3. 否则(如果 工程。汤姆尔 不存在),则 uuid 是全零https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID[nil UUID]。

*项目目录的依赖关系图*由每个包子目录中项目文件的存在和内容决定。 规则是:

*如果包子目录没有项目文件,则将其从graph中省略,并将其代码中的import语句视为顶级,与主项目和REPL相同。 *如果包子目录有一个项目文件,则其UUID的图形条目是 [deps] 项目文件的映射,如果该部分不存在,则认为该文件是空的。

例如,假设一个包目录具有以下结构和内容:

Aardvark/
    src/Aardvark.jl:
        import Bobcat
        import Cobra

山猫/
    工程。汤姆尔:
        [deps]
        眼镜蛇="4725e24d-f727-424b-bca0-c4307a3456fa"
        Dingo="7a7925be-828c-4418-bbeb-bac8dfc843bc"

    src/山猫.jl:
        进口眼镜蛇
        进口野狗

眼镜蛇,眼镜蛇/
    工程。汤姆尔:
        uuid="4725e24d-f727-424b-bca0-c4307a3456fa"
        [deps]
        Dingo="7a7925be-828c-4418-bbeb-bac8dfc843bc"

    src/眼镜蛇.jl:
        进口野狗

野狗/
    工程。汤姆尔:
        uuid="7a7925be-828c-4418-bbeb-bac8dfc843bc"

    src/野狗.jl:
        #无进口

这里是一个对应的roots结构,表示为字典:

roots = Dict(
    :Aardvark => UUID("00000000-0000-0000-0000-000000000000"), # no project file, nil UUID
    :Bobcat   => UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf"), # dummy UUID based on path
    :Cobra    => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), # UUID from project file
    :Dingo    => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), # UUID from project file
)

这里是对应的图结构,表示为字典:

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(),
)

一些需要注意的一般规则:

  1. 没有项目文件的包可以依赖于任何顶层依赖关系,并且由于包目录中的每个包都在顶层可用,因此它可以导入环境中的所有包。

  2. 具有项目文件的包不能依赖于没有项目文件的包,因为具有项目文件的包只能在 图表 没有项目文件的包不会出现在 图表.

  3. 具有项目文件但没有显式UUID的包只能由没有项目文件的包依赖,因为分配给这些包的虚拟Uuid是严格内部的。

在我们的示例中观察这些规则的以下具体实例:

* 土豚 可以导入任何 山猫, 眼镜蛇,眼镜蛇野狗;它确实进口 山猫眼镜蛇,眼镜蛇. * 山猫 可以和确实导入两者 眼镜蛇,眼镜蛇野狗,它们都有带有Uuid的项目文件,并在 山猫'的 [deps] 节。 * 山猫 不能依赖 土豚土豚 没有项目文件。 * 眼镜蛇,眼镜蛇 可以和确实导入 野狗,它有一个项目文件和UUID,并在 眼镜蛇,眼镜蛇'的 [deps] 节。 * 眼镜蛇,眼镜蛇 不能依赖 土豚山猫 因为既没有真正的UUIDs。 * 野狗 不能导入任何东西,因为它有一个没有 [deps] 节。

*包目录中的路径映射*很简单:它将子目录名称映射到相应的入口点路径。 换句话说,如果我们的示例项目目录的路径是 /家/我/动物 然后将 路径 map可以用这个字典来表示:

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",
)

由于根据定义,包目录环境中的所有包都是具有预期入口点文件的子目录,因此它们的 路径 地图条目始终具有此表单。

环境堆栈

第三种也是最后一种环境是通过复盖其中几个环境来组合其他环境的环境,使每个环境中的包在单个复合环境中可用。 这些复合环境称为_environment stacks_。 朱莉娅 LOAD_PATH global定义了一个环境栈—​Julia进程运行的环境。 如果您希望Julia进程只能访问一个项目或包目录中的包,请将其作为 LOAD_PATH. 但是,访问一些您喜欢的工具通常非常有用-标准库,分析器,调试器,个人实用程序等。--即使它们不是您正在处理的项目的依赖项。 通过将包含这些工具的环境添加到加载路径,您可以立即在顶级代码中访问它们,而无需将它们添加到项目中。

组合环境堆栈组件的根、图和路径数据结构的机制很简单:它们被合并为字典,在键冲突的情况下,优先于较早的条目。 换句话说,如果我们有 堆栈=[env₁,env₂,…​] 那么我们有:

roots = reduce(merge, reverse([roots₁, roots₂, …]))
graph = reduce(merge, reverse([graph₁, graph₂, …]))
paths = reduce(merge, reverse([paths₁, paths₂, …]))

下标的 根!, graphᵢ路径? 变量对应于下标环境, envᵢ,载于 堆叠. 该 反向 存在是因为 合并;合并 当参数字典中的键之间存在冲突时,优先选择最后一个参数而不是第一个参数。 这个设计有几个值得注意的特点:

  1. _primary环境-即堆栈中的第一个环境-忠实地嵌入到堆叠环境中。 堆栈中第一个环境的完整依赖关系图保证完整地包含在堆栈环境中,包括所有依赖关系的相同版本。

  2. 非主环境中的包最终可能会使用其依赖项的不兼容版本,即使它们自己的环境完全兼容。 当它们的一个依赖项被堆栈中较早环境中的版本(通过图形或路径,或两者)阴影时,就会发生这种情况。

由于主环境通常是您正在处理的项目的环境,而堆栈中稍后的环境包含其他工具,因此这是正确的权衡:最好打破您的开发工具,但保持项目工作。 当出现这种不兼容的情况时,您通常需要将开发工具升级到与主项目兼容的版本。

软件包扩展

包"扩展"是在当前Julia会话中加载指定的一组其他包(其"触发器")时自动加载的模块。 扩展定义在 [扩展] 节中的项目文件。 扩展的触发器是在 [弱者] (也许,但不寻常的 [deps])项目文件的部分。 这些包可以像其他包一样具有compat条目。

name = "MyPackage"

[compat]
ExtDep = "1.0"
OtherExtDep = "1.0"

[weakdeps]
ExtDep = "c9a23..." # uuid
OtherExtDep = "862e..." # uuid

[extensions]
BarExt = ["ExtDep", "OtherExtDep"]
FooExt = "ExtDep"
...

下面的钥匙 扩展 是扩展的名称。 当加载该扩展右侧的所有包(触发器)时,它们被加载。 如果一个扩展只有一个触发器,为了简洁起见,触发器列表可以写成一个字符串。 扩展的入口点的位置在 ext/FooExt。jlext/FooExt/FooExt。jl 申请延期 N.食物,食物. 扩展的内容通常结构为:

module FooExt

#加载主包和触发器
使用MyPackage,ExtDep

#使用触发器的类型扩展主包中的功能
我的包装。func(x::ExtDep.SomeStruct)=。..

结束

当一个带有扩展的包被添加到一个环境中时, 弱者,弱者扩展 节存储在该包的节的清单文件中。 包的依赖关系查找规则与其"父"相同,只是列出的触发器也被视为依赖关系。

工作区

项目文件可以通过给出一组作为工作区一部分的项目来定义工作区:

[workspace]
projects = ["test", "benchmarks", "docs", "SomePackage"]

列出的每个项目 工程项目 数组由其从工作区根的相对路径指定。 这可以是一个直接的子目录(例如, "测试")或嵌套的子目录(例如, "嵌套/细分/MyPackage"). 每个项目都包含自己的 工程。汤姆尔 件,其可以包括附加的依赖性和兼容性约束。 在这种情况下,包管理器从工作区中的所有项目收集所有依赖项信息,生成一个清单文件,该清单文件组合了所有依赖项的版本。

当Julia加载一个项目时,它会向上搜索父目录,直到它到达用户的主目录以查找包含该项目的工作区。 这允许在工作区目录树中以任意深度嵌套工作区项目。

此外,工作区可以是"嵌套的",这意味着定义工作区的项目也可以是另一个工作区的一部分。 在这种情况下,仍然使用单个清单文件,与"根项目"(没有其他工作区的项目)一起存储。 示例文件结构可能如下所示:

Project.toml # projects = ["MyPackage"]
Manifest.toml
MyPackage/
    Project.toml # projects = ["test"]
    test/
        Project.toml

包/环境首选项

首选项是影响环境中包行为的元数据字典。 Preferences系统支持在编译时读取首选项,这意味着在代码加载时,我们必须确保Julia选择的预编译文件在加载之前使用与当前环境相同的首选项构建。 用于修改首选项的公共API包含在https://github.com/JuliaPackaging/Preferences.jl[Preferences.jl]包。 首选项作为TOML字典存储在 (朱莉娅)LocalPreferences。汤姆尔 当前活动项目旁边的文件。 如果首选项被"导出",则它将存储在 (朱莉娅)项目。汤姆尔 相反。 其目的是允许共享项目包含共享首选项,同时允许用户自己在LocalPreferences中使用自己的设置复盖这些首选项。toml文件,这应该是。顾名思义,gitignored。

在编译期间访问的首选项会自动标记为编译时首选项,并且记录到这些首选项的任何更改都将导致Julia编译器重新编译任何缓存的预编译文件(.纪 和相应的 .所以, .dll,或 .迪利布 文件)为该模块。 这是通过在编译期间序列化所有编译时首选项的哈希来完成的,然后在搜索要加载的正确文件时根据当前环境检查该哈希。

首选项可以使用仓库范围的默认设置;如果包Foo安装在您的全局环境中并且它设置了首选项,那么只要您的全局环境是您的 LOAD_PATH. 环境堆栈中较高的环境中的首选项被加载路径中较近的条目复盖,以当前活动的项目结束。 这允许存在仓库范围的首选项默认设置,活动项目能够合并甚至完全复盖这些继承的首选项。 请参阅docstring 偏好。set_preferences!() 有关如何将首选项设置为允许或不允许合并的完整详细信息。

结论

联合包管理和精确的软件可重复性在包系统中是困难但有价值的目标。 结合起来,这些目标导致了比大多数动态语言更复杂的包加载机制,但它也产生了更常见于静态语言的可伸缩性和可重复性。 通常,Julia用户应该能够使用内置的包管理器来管理他们的项目,而不需要精确了解这些交互。 电话: Pkg。添加("X") 将添加到适当的项目和清单文件,通过 Pkg。激活("Y"),以便将来的电话 进口X 将加载 X 没有进一步的思考。