大家好,周末总是过的那么快,周六跟北京几个Erlang开发者去森林公园玩了一天,打打三国杀,下下象棋,聊聊天。能多出去走走,认识认识其他的Erlang开发者,还是挺爽的一件事。晚上闲下来,静下心来跟大家继续分享mochiweb源码。
在上一篇,我们简单了学习了几个Erlang的系统函数,以及mochiweb_example_deps:deps_on_path/0 函数,这一篇我们继续从mochiweb_example_deps:new_siblings/1 函数往下看:
%% @spec new_siblings(Module) -> [Dir] %% @doc Find new siblings paths relative to Module that aren't already on the %% code path. new_siblings(Module) -> Existing = deps_on_path(), SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), Siblings = [filename:dirname(X) || X <- SiblingEbin, ordsets:is_element( filename:basename(filename:dirname(X)), Existing) =:= false], lists:filter(fun filelib:is_dir/1, lists:append([[filename:join([X, "ebin"]), filename:join([X, "include"])] || X <- Siblings])).
这里我们从上一篇知道了mochiweb_example_deps:deps_on_path/0 函数返回的值为:Existing = []。我们继续往下看,先看下mochiweb_example_deps:local_path/2 函数:
%% @spec local_path([string()], Module) -> string() %% @doc Return an application-relative directory from Module's application. local_path(Components, Module) -> filename:join([get_base_dir(Module) | Components]).
这里两个参数的值为:
< Components = ["deps","*","ebin"]
< Module = mochiweb_example_deps
这个函数就一行代码,首先调用 mochiweb_example_deps:get_base_dir/2 函数,传递参数 Module。
%% @spec get_base_dir(Module) -> string() %% @doc Return the application directory for Module. It assumes Module is in %% a standard OTP layout application in the ebin or src directory. get_base_dir(Module) -> {file, Here} = code:is_loaded(Module), filename:dirname(filename:dirname(Here)).
从注释来看,大意是:返回模块所在的应用程序目录,它假定模块在一个标准的OTP应用程序的ebin或src目录。
我们先看下函数 code:is_loaded/1,检查该模块是否加载,如果已加载,返回{file, Loader};否则返回false, erlang doc 地址:http://www.erlang.org/doc/man/code.html#is_loaded-1,如下图:
这里:< Here = "/home/administrator/workplace/mochiweb_example/ebin/mochiweb_example_deps.beam",而 filename:dirname/1 这个函数前一篇我们已经讲过了:Returns the directory part of Filename。
我们对这个函数稍加修改来看下执行过程具体获得到的值,如下图:
从上面这个图,我们能很明白的看到整个函数执行的每一步,这里主要跟大家提下,filename:dirname/1 这个函数接受的参数不一定是文件路径,也有可能是目录路径,如果是目录路径,则返回的是上一层目录的路径,如D2的值。
知道了这个函数的作用,我们接着回到mochiweb_example_deps:local_path/2 函数,这里把mochiweb_example_deps:get_base_dir/2 函数返回的值和Components组合成一个List,传递给函数:filename:join/1,这个函数的erlang doc 地址:http://www.erlang.org/doc/man/filename.html#join-1,如下图:
这个方法也很简单,拼接路径,我们看下:
filename:join(["/home/administrator/workplace/mochiweb_example" | ["deps","*","ebin"]]).
= "/home/administrator/workplace/mochiweb_example/deps/*/ebin".
回到mochiweb_example_deps:new_siblings/1 函数往下看,这里我们看下函数:filelib:wildcard/1,erlang doc 地址:http://www.erlang.org/doc/man/filelib.html#wildcard-1,如下图:
这个方法也挺简单的,按照一定的规则去模式匹配并返回匹配的文件列表,我做了写测试,如下图:
我们看下:
SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module))
= filelib:wildcard("/home/administrator/workplace/mochiweb_example/deps/*/ebin")
= ["/home/administrator/workplace/mochiweb_example/deps/mochiweb/ebin"]
继续往下看:
Siblings = [filename:dirname(X) || X <- SiblingEbin, ordsets:is_element( filename:basename(filename:dirname(X)), Existing) =:= false],
我们先看下函数:ordsets:is_element/2,判断Element是否是Ordset的元素,是则返回true, 否则返回flase。erlang doc 地址:http://www.erlang.org/doc/man/ordsets.html#is_element-2,如下图:
从上来文,我们知道:
< Existing = []
< SiblingEbin = ["/home/administrator/workplace/mochiweb_example/deps/mochiweb/ebin"]
所以得到:
< Siblings = ["/home/administrator/workplace/mochiweb_example/deps/mochiweb"]
如下图:
好了,这一篇就到这了,下一篇我们从下面代码处继续往下看:
lists:filter(fun filelib:is_dir/1, lists:append([[filename:join([X, "ebin"]), filename:join([X, "include"])] || X <- Siblings])).
最后,谢谢大家的耐心阅读,咱们下一篇再见。