2

调整 Elixir 及其生态系统中的所有工具以使用不同的构建系统。

在这个系统中,包和它们的依赖是分开管理的,Hex 是在离线模式下工作的。(抓住压缩包)

它有一个警告:每次我导入一个新包时,我还需要从 hexpm 导入最新的注册表文件,并且我不能使用不是通过 hex 发布的包,除非它们位于 deps 链的顶层。

给定一堆 tarball(并假设它们之间的依赖关系得到满足,那么如何构建一个与它们一起使用的十六进制注册表文件。

到目前为止我所拥有的:

  • 查看注册表文件格式,发现它是一个 ets 文件。可以装载和检查它;现在我需要生成
  • 查看了网站如何构建注册表文件,但这对于我的需求来说非常复杂
  • 我有点难以理解为什么需要一个注册表文件(如果有,为什么每个包不能在元数据中包含所需的信息,从而使中央注册表的需要过时了)

无论如何,如果有人玩过 Hex 并且可以提供一些关于如何做到这一点的指导,我将不胜感激。

4

2 回答 2

2

如果没有关于您的用例的更多信息,很难提供好的信息和建议。您能否详细说明您在做什么以及为什么要这样做?不过我会尽力回答这个问题。

这是注册表格式的规范:https ://github.com/hexpm/specifications/blob/master/registry.md 。

格式相当简单,不需要太多代码即可自己构建 ETS 文件。

我有点难以理解为什么需要一个注册表文件(如果有,为什么每个包不能在元数据中包含所需的信息,从而使中央注册表的需要过时了)

Hex 客户端中的依赖关系解析需要注册表。如果客户端必须获取每个包版本以查看它是否解析了许多无用的 HTTP 请求,那么解析器可能会尝试许多不同版本的包。注册表作为优化存在,因此我们只需获取单个文件即可完成完整解析。

我认为您可能想要的是直接依赖本地包 tarball,因为您暗示您自己进行依赖解析。那是对的吗?我在客户端上打开了一个问题来支持这一点:https ://github.com/hexpm/hex/issues/261

于 2016-07-16T20:33:05.920 回答
0

对于最终来到这里的后代,这是一个有效的注册表构建器:

defp string_files(files) do
  Enum.into(files, %{}, fn {name, binary} ->
    {List.to_string(name), binary}
  end)
end

defp decode(string) when is_binary(string) do
  string = String.to_char_list(string)
  case :safe_erl_term.string(string) do
    {:ok, tokens, _line} ->
      try do
        terms = :safe_erl_term.terms(tokens)
        result = Enum.into(terms, %{})
        {:ok, result}
      rescue
        FunctionClauseError ->
          {:error, "invalid terms"}
        ArgumentError ->
          {:error, "not in key-value format"}
      end

    {:error, reason} ->
      {:error, inspect reason}
  end
end

def build_registry(hex_home) do
  # find the tars
  tars = Path.wildcard(Path.join(hex_home,"packages/*.tar"))

  # initialize the ets table used to build the registry
  :ets.new(:myr, [:named_table])
  :ets.insert(:myr, {:"$$version$$", 4})

  # go through the tars, extract the info needed and populate
  # the registry
  Enum.each(tars, fn filename ->
      {:ok, files} = :erl_tar.extract(String.to_char_list(filename), [:memory])
      files = string_files(files)
      {:ok, metadata} = decode(files["metadata.config"])
      name = metadata["app"]
      version = metadata["version"]
      build_tools = metadata["build_tools"]
      checksum = files["CHECKSUM"]
      deps = []
      if metadata["requirements"], do: deps = metadata["requirements"]
      reg_deps = Enum.map(deps, fn
          {name, depa} ->
              depa = Enum.into(depa, %{})
              [name, depa["requirement"], depa["optional"], depa["app"]]
          depa ->
              depa = Enum.into(depa, %{})
              [depa["name"], depa["requirement"], depa["optional"], depa["app"]]
      end)
      IO.puts "adding dependency"
      IO.inspect {name, [[version]]}
      IO.inspect {{name, version}, [reg_deps, checksum, build_tools]}
      :ets.insert(:myr, {name, [[version]]})
      :ets.insert(:myr, {{name, version}, [reg_deps, checksum, build_tools]})
  end)

  # persist the registry to disk and remove the table
  registry_file = Path.join(hex_home, "registry.ets")
  IO.puts "Writing registry to: #{registry_file}"
  :ets.tab2file(:myr, String.to_char_list(registry_file))
  :ets.delete(:myr)
  registry_file_gzip = registry_file <> ".gz"
  IO.puts "Gzipping registry to: #{registry_file_gzip}"
  gzipped_content = File.read!(registry_file) |> :zlib.gzip
  File.write!(registry_file_gzip, gzipped_content)
end

有关更多上下文:

于 2016-07-19T20:56:57.367 回答