项目中需要将同一份的源文件发布到不同的目录, 以前的做法是修改Emakefile配置后,使用erl -make生成,有点麻烦.

我们知道,编译erlang使用erl -make,而erl -make实际上调用的make:all()., 而make:all().会读取当前目录下的Emakefile文件,根据相应的配置来编译文件.

如上,目前一些特殊的需求无法满足:

  1. 一个文件编译到不同的目录下;
  2. 读取Emakefile,然后修改其中部分参数后再编译(比如,修改outdir);
  3. 如果有Emakefile1, Emakefile2…EmakefileN, 怎么按照每个编译选项编译?

那如何即使用Emakefile,又能够满足上面的需求呢?

我们先来做一些测试, 用到erlangmakecompile模块.
现在我们有目录结构:

1
2
3
4
5
6
.
├── ebin
├── ebin1
├── Emakefile
└── src
└── a.erl

正常的Emakefile

一个正常的Emakefile文件内容:

1
{["./src/*"], [{outdir, "./ebin"}]}.

现在使用erl -make编译, 结果:

1
2
3
4
5
6
7
.
├── ebin
│   └── a.beam
├── ebin1
├── Emakefile
└── src
└── a.erl

正确编译

缺少参数的Emakefile

Emakefile

1
{["./src/*"], []}.

编译:

1
2
3
1> make:all([{outdir, "./ebin"}]).
Recompile: ./src/a
up_to_date

结果和预想的结构一致

1
2
3
4
5
6
7
.
├── ebin
│   └── a.beam
├── ebin1
├── Emakefile
└── src
└── a.erl

修改已经存在的Emakefile参数?

一个正常的Emakefile

1
{["./src/*"], [{outdir, "./ebin"}]}.

新建一个目录mkdir ebin1,作为编译输出目录
显示编译完成

1
2
3
1> make:all([{outdir, "./ebin1"}]).
Recompile: ./src/a
up_to_date

看下结果:

1
2
3
4
5
6
7
.
├── ebin
│   └── a.beam
├── ebin1
├── Emakefile
└── src
└── a.erl

结果并没有如我们预料的在ebin1目录下生成文件,这说明make的时候优先Emakefile中的参数

回到最开始的问题

回到最开始的问题, 解决方法:

  1. Emakefile只保留公有编译选项, 比如{i, "include"}等;
  2. 个性部分,通过make:all(Options).Options选项传进去, 比如{outdir, "./ebin"}, {d, debug}等;

现在贴下小脚本,凑下字数:

my_make.erlview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
%% ===========================================================================
%% @doc
%% 个人编译模块
%% 编译所有文件到ebin,ebin1,/data/t/ebin2目录
%% erl -s my_make all ebin ebin1 /data/t/ebin2
%% 编译单个文件到ebin目录
%% erl -s my_make file src/a ebin
%% @end
%% ===========================================================================
-module(my_make).
-author(zhaoming).
-version(1.0).
-date('2016-03-29').

-export([all/1, file/1]).

all([]) ->
msg(not_dir),
erlang:halt();
all(OutDirList) ->
timer:sleep(1000),
io:format("~n"),
Result = do_make(OutDirList),
msg(Result),
erlang:halt().

do_make([]) -> up_to_date;
do_make([OutDirAtom|OutDirList]) ->
OutDir = atom_to_list(OutDirAtom),
Options = [{outdir, OutDir}, {d, debug}],
msg(io_lib:format("Dir:~p", [filename:absname(OutDir)])),
case filelib:is_dir(OutDir) of
true -> ok;
false ->
ok = filelib:ensure_dir(OutDir),
ok = file:make_dir(OutDir),
ok
end,
case make:all(Options) of
up_to_date -> do_make(OutDirList);
error -> error
end.

file([File, OutDirAtom|_]) ->
do_file(File, OutDirAtom),
erlang:halt().

do_file(File, OutDirAtom) ->
{ok, [{_, Options}|_]} = file:consult("Emakefile"),
OutDir = atom_to_list(OutDirAtom),
R = compile:file(atom_to_list(File), Options ++ [{outdir, OutDir}, {d, debug}]),
msg(R).

msg(Msg) ->
case Msg of
Msg when is_list(Msg); is_binary(Msg) ->
io:format("~s~n", [Msg]);
_ -> io:format("~p~n", [Msg])
end.