Mix工程的运行方式

Mix 是 elixir 的工程管理工具,是时候总结一下 mix 工程的运行方式了。

我们有两种方式来创建 mix 工程:

  • 第一种是 mix new xxx 创建一个 elixir 库。
  • 第二种是 mix new --sup xxx 创建一个带监督树的应用。

我们有多种方式来运行 Mix 工程。

iex -S mix

在开发阶段我们最常用的方式是用 iex -S mix 在交互式 shell iex 中运行代码。这个命令会在启动 iex 之前先运行 mix 编译工程,并加载进虚拟机。然后我们就可以在 iex 中调用我们编写的函数了,这在开发调试阶段非常实用。

这个命令实际上运行的还是 mix run ,只是它还给我们启动了一个交互式 shell。

mix run

当我们使用 mix new --sup xxx 来创建一个工程时,就可以使用 mix run 或则 mix 来运行工程, mix 默认执行的就是 mix run

无论是 mix run 还是 mix compile ,mix 工程都会被编译成一个 erlang 应用,如果熟悉 erlang 的话,一定不会对此感到陌生。

一个 erlang 应用肯定不止一个文件,但通常由一个 .app 文件来描述。 .app 文件中描述了应用的名称,版本,依赖以及启动入口等信息。如果我们使用 mix new --sup example_sup 来创建一个应用,然后运行 mix compile ,就可以在 _build/dev/lib/example_sup/ebin/ 目录下发现一个叫做 example_sup.app 的文件,内容大致如下:

{application,example_sup,
             [{config_mtime,1749558441},
              {optional_applications,[]},
              {applications,[kernel,stdlib,elixir,logger]},
              {description,"example_sup"},
              {modules,['Elixir.ExampleSup','Elixir.ExampleSup.Application']},
              {registered,[]},
              {vsn,"0.1.0"},
              {mod,{'Elixir.ExampleSup.Application',[]}}]}.

这是一个 erlang 元组,其中的 mod 字段就是应用的启动模块,这个模块必须实现 elixir 的 Application 行为,其中的 start 函数就相当于其他语言的 main 函数。Erlang 虚拟机的应用控制器会读取这个 .app 文件并启动应用。

Mix 会根据 mix.exs 文件来生成这个 .app 文件,我们可以在 mix.exs 文件的 application 函数中通过 :mod 来配置启动模块,这个函数需要返回一个 keyword 列表。

def application do
  [
    extra_applications: [:logger],
    mod: {ExampleSup.Application, []}
  ]
end

如果我们仔细对比 mix new xxxmix new xxx --sup 生成的工程,就会发现它俩唯一的区别就是后者多了一个 application.ex 文件,并且 mix.exs 文件中的 application 函数多了 :mod 的配置。前者也能通过 mix compilemix run 来编译运行,但是运行不会有任何结果,因为生成的 .app 文件中没有 mod 字段,因此也不会有任何代码运行。

Mix 极大的简化了工程管理,运行,测试,打包,发布的流程和工作量,但是其最终还是会落回 erlang 上,因此要深入学习 elixir 还是需要有一定的 erlang 基础。

自定义 mix 任务

我们也可以通过自定义 mix 任务来运行代码,这样我们就可以方便的在命令行直接输入 mix xxx 来运行代码。一个经典的例子就是使用 mix phx.server 来启动 phoenix 应用。

自定义 mix 任务我们需要将源码文件放到 lib/mix/tasks/ 目录下,然后创建一个 .ex 文件,名字任意。比如我们创建一个 start.ex 文件,然后输入以下内容:

defmodule Mix.Tasks.Start do
	use Mix.Task
	
	def run(args) do
		IO.inspect(args, label: "args")
	end
end

我们需要 use Mix.Task 并实现 run 函数,它是 Mix.Task 行为的回调函数,然后我们将要运行的代码放到这里就行了。

做完这一切之后我们就可以使用 mix start 来运行我们的代码了, mix 后面的是不带扩展名的文件名。我们可以通过 mix start xxx xxx 来给 run 函数传递参数, mix start 后面的内容会以空格切分为字符串数组传递给 run 函数,这里是绑定到 args 参数上,更复杂的参数规则解析需要我们手动实现,或者借用第三方库实现。

escript

从这里开始就属于是打包发布运行的方式了,也就是说已经离开开发阶段了。

Escript 是一种发布 elixir 命令行程序的方式,因为 elixir 程序是编译成 .beam 文件在 Beam 虚拟机中运行的,无法直接编译成可执行文件。escript 提供了一种机制,让我们可以像可执行程序那样运行 elixir 程序。elixir 程序在编译时,每个模块都会编译成一个 .beam 文件,文件名就是模块名。escript 可以将 elixir 代码打包进一个文件,然后通过 escript xxx 来运行, escript 是用来运行编译产物的程序,它会随着 erlang 一起安装。

escript 和 escript 编译产物的关系类似于 bash.sh 文件的关系,escript 的编译参数类似一个脚本文件,它的第一行就写着执行它的程序:

#! /usr/bin/env escript
...

所以在 Linux 系统上应该是可以像可执行程序那样运行的,但是 Windows 并没有这种机制,因此需要用 escript xxx 来运行。

编译 escript 的方式也非常简单,其实我在 Elixir 工具篇 中就已经介绍过了。我们只需要一个包含 main 函数的模块以及一些简单的配置即可。

例如我们使用 mix new example 创建了一个工程,然后在 example.ex 中添加一个 main 函数。

defmodule Example do
  ...

  def main(argv) do
    IO.inspect(argv, label: "argv")
  end
end

然后我们在 mix.exs 中加入 escript 配置。

def project do
  [
    ...
    escript: escript()
  ]
end

defp escript do
  [
    main_module: Example
  ]
end

最后我们使用 mix escript.build 来编译工程,它会在工程根目录下生成一个与工程同名的无扩展名的文件,这里是 example 。接下来我们就可以使用下面的命令来运行它了:

> escript example -a=1 -b --c d
argv: ["-a=1", "-b", "--c", "d"]

同样,参数是以空格切分的字符串列表的形式传递给 main 函数,需要我们自己解析。

install

我们还可以通过 mix escript.install 将编译好的文件安装到本地,路径是 ~/.mix/escripts ,在 windows 上则一般是 C:\Users\<user>\.mix\escripts 。安装不仅仅是将编译文件拷贝到安装目录,在 Windows 上同时还会生成一个同名的 .bat 运行脚本,当我们将安装目录添加到 Paht 以后,就可以直接在命令行中输入名称运行了,而不再需要使用 escript 。这个目录有可能已经自动添加到系统环境变量了,如果没有就手动添加一下。

以上面的例子为例,我们运行 mix escript.install 以后,生成的 example.bat 内容如下,其实就是调用了 escript 而已。

@echo off
@escript "%~dpn0" %*

除了安装本地项目,还可以是一个编译文件路径, github 仓库或者 hex 仓库,以下示例摘自官方网站

mix escript.install escript
mix escript.install path/to/escript
mix escript.install git https://path/to/git/repo
mix escript.install git https://path/to/git/repo branch git_branch
mix escript.install git https://path/to/git/repo tag git_tag
mix escript.install git https://path/to/git/repo ref git_ref
mix escript.install github user/project
mix escript.install github user/project branch git_branch
mix escript.install github user/project tag git_tag
mix escript.install github user/project ref git_ref
mix escript.install hex hex_package
mix escript.install hex hex_package 1.2.3

release

这是另一种打包发布方式,我们也称其为发布镜像,它是一个自包含的发布包,可以在没有安装 erlang/elixir 环境的机器上运行。这就意味着它不仅会打包代码,还会打包 erlang 虚拟机,及其必要的库,事实上,还包括一些非常实用的脚本。完整的细节可以参考官方文档

制作发布镜像并不支持交叉编译,事实上 elixir 代码也没有所谓的交叉编译,因为它是编译成 .beam 文件在虚拟机上运行,真正需要跨平台的是虚拟机,然而在打包时,使用的是本机上安装的虚拟机,所以发布镜像也只能在同样的操作系统上运行,除非我们在目标操作系统上重新打包。

制作发布镜像不需要有什么特殊的操作,直接运行 mix release 命令即可。如果需要一些配置的话,可以在 mix.exs 文件中的 project 函数中通过 :release 键配置。

def project do
  [
    releases: [
      demo: [
        include_executables_for: [:unix],
        applications: [runtime_tools: :permanent]
      ],

      ...
    ]
  ]
end

:release 配置也是一个 keyword 列表,键是应用名。完整配置列表见官网。

打包文件生成在 _build/dev/rel/app_name 下, dev 是开发环境,这里会根据打包环境变化, app_name 是项目名。我们将 app_name 整个目录打包成压缩文件就可以发布了,它包含4个目录:

  • bin - 应用启动脚本,包含了调试,运维等等。
  • erts-版本 - erlang 虚拟机目录。
  • lib - 源代码编译结果目录,包含了项目以及标准库。
  • releases - 虚拟机 cookie,配置文件以及 elixir 相关脚本,就像我在Elixir 工具篇中讲过的一样,elixir 除了标准库以外就是一些脚本了。

所有库和配置都是带有版本号的,这也就意味着我们可以不停机热更新代码。环境相关的配置包括 cookie 都支持环境变量覆盖,一般情况下我们只需要用到 bin 目录下的启动脚本即可。

release 模式和普通模式的一个重要区别是,release 模式下 Beam 虚拟机启动时会将库加载进来,而普通模式下虚拟机在用到某个函数时才会去加载对应的模块,因此 release 可以减少首次响应时间。此外,release 模式将代码和运行环境一起打包,降低了版本冲突的风险,而且不需要目标机器预先安装环境。干过开发的都知道,配环境简直太痛苦了。

启动脚本非常简单,属于看一眼就会用,这里偷个懒,原谅我直接抄一下官网的内容吧。

start        Starts the system
start_iex    Starts the system with IEx attached
daemon       Starts the system as a daemon (Unix-like only)
daemon_iex   Starts the system as a daemon with IEx attached (Unix-like only)
install      Installs this system as a Windows service (Windows only)
eval "EXPR"  Executes the given expression on a new, non-booted system
rpc "EXPR"   Executes the given expression remotely on the running system
remote       Connects to the running system via a remote shell
restart      Restarts the running system via a remote command
stop         Stops the running system via a remote command
pid          Prints the operating system PID of the running system via a remote command
version      Prints the release name and version to be booted

archive

archive 严格来说不是 mix 工程的运行方式,而是将 mix 工程打包成依赖库,是一种库的发布方式。

运行 mix archive.build 会在项目根目录下生成一个 .ez 文件,我猜意思可能是”elixir zip”吧。而他确实也就是一个压缩文件,我们可以用压缩工具去解压它。其内部目录结构如下。

⊢app-name-version
⊢——ebin
⊢——.elixir

.elixir 文件中记录的是兼容的 elixir 版本, ebin 中是 .beam 文件和 .app 文件。

archive 并不是用来发布 elixir 代码库的,官方建议 archive 仅用于 Mix 任务扩展,包管理等功能,原文如下。

In general, we recommend the usage of archives to be limited for extensions of Mix, such as custom SCMs, package managers, and the like.

install

archive 也可以通过 mix archive.install 命令安装,安装在 ~/.mix/archives 目录下,在 Windows 上一般是 C:\Users\<user>\.mix\archives 目录。

如果你做过 Phoenix 开发的话,那么在这个目录下就能看到熟悉的 hex-xx.xxphx_new-xx.xx 目录,这正是我们安装的 phx 相关的命令,注意它并不是 phoenix 库,仅仅是 mix 命令而已。

同 escript 一样,archive 也可以从网上安装,以下示例摘自官方网站

mix archive.install archive.ez
mix archive.install path/to/archive.ez
mix archive.install git https://path/to/git/repo
mix archive.install git https://path/to/git/repo branch git_branch
mix archive.install git https://path/to/git/repo tag git_tag
mix archive.install git https://path/to/git/repo ref git_ref
mix archive.install github user/project
mix archive.install github user/project branch git_branch
mix archive.install github user/project tag git_tag
mix archive.install github user/project ref git_ref
mix archive.install hex hex_package
mix archive.install hex hex_package 1.2.3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值