mad_compile.erl 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. -module(mad_compile).
  2. -export([deps/2]).
  3. -export([app/1]).
  4. -define(COMPILE_OPTS(Inc, Ebin), [report, {i, Inc}, {outdir, Ebin}]).
  5. %% compile dependencies
  6. deps(_, []) ->
  7. ok;
  8. deps(Cwd, [H|T]) ->
  9. {Name, _} = mad_deps:name_and_repo(H),
  10. case get(Name) of
  11. compiled ->
  12. ok;
  13. _ ->
  14. dep(Cwd, Name)
  15. end,
  16. deps(Cwd, T).
  17. %% compile a dependency
  18. dep(Cwd, Name) ->
  19. %% check dependencies of the dependency
  20. DepPath = filename:join([Cwd, "deps", Name]),
  21. Conf = mad_utils:rebar_conf(DepPath),
  22. Conf1 = mad_utils:script(DepPath, Conf),
  23. deps(Cwd, mad_utils:get_value(deps, Conf1, [])),
  24. %% add lib_dirs to path
  25. LibDirs = mad_utils:lib_dirs(DepPath, Conf1),
  26. code:add_paths(LibDirs),
  27. %% compile sub_dirs and add them to path
  28. SubDirs = mad_utils:sub_dirs(DepPath, Conf),
  29. lists:foreach(fun app/1, SubDirs),
  30. SrcDir = mad_utils:src(DepPath),
  31. Files = sort(erl_files(SrcDir)) ++ app_src_files(SrcDir),
  32. case Files of
  33. [] ->
  34. ok;
  35. Files ->
  36. IncDir = mad_utils:include(DepPath),
  37. EbinDir = mad_utils:ebin(DepPath),
  38. Opts = mad_utils:get_value(erl_opts, Conf1, []),
  39. mad_utils:exec("mkdir", ["-p", EbinDir]),
  40. lists:foreach(compile_fun(SrcDir, IncDir, EbinDir, Opts), Files),
  41. put(Name, compiled)
  42. end.
  43. app(Dir) ->
  44. Conf = mad_utils:rebar_conf(Dir),
  45. Conf1 = mad_utils:script(Dir, Conf),
  46. SrcDir = mad_utils:src(Dir),
  47. Files = sort(erl_files(SrcDir)) ++ app_src_files(SrcDir),
  48. case Files of
  49. [] ->
  50. ok;
  51. Files ->
  52. IncDir = mad_utils:include(Dir),
  53. EbinDir = mad_utils:ebin(Dir),
  54. Opts = mad_utils:get_value(erl_opts, Conf1, []),
  55. mad_utils:exec("mkdir", ["-p", EbinDir]),
  56. lists:foreach(compile_fun(SrcDir, IncDir, EbinDir, Opts), Files),
  57. code:add_path(EbinDir)
  58. end.
  59. validate_property({modules, _}, Modules) ->
  60. {modules, Modules};
  61. validate_property(Else, _) ->
  62. Else.
  63. compile_fun(SrcDir, IncDir, EbinDir, Opts) ->
  64. fun(F) ->
  65. code:add_path(EbinDir),
  66. F1 = filename:join(SrcDir, F),
  67. case is_app_src(F1) of
  68. false ->
  69. Compiled = is_compiled(EbinDir, F1),
  70. if Compiled =:= false ->
  71. io:format("Compiling ~s~n", [F1]),
  72. compile:file(F1, ?COMPILE_OPTS(IncDir, EbinDir) ++ Opts);
  73. true ->
  74. ok
  75. end;
  76. true ->
  77. %% add {modules, [Modules]} to .app file
  78. AppFile = filename:join(EbinDir, app_src_to_app(F1)),
  79. io:format("Writing ~s~n", [AppFile]),
  80. BeamFiles = filelib:wildcard("*.beam", EbinDir),
  81. Modules = [list_to_atom(filename:basename(X, ".beam"))
  82. || X <- BeamFiles],
  83. [Struct|_] = mad_utils:consult(F1),
  84. {application, AppName, Props} = Struct,
  85. Props1 = add_modules_property(Props),
  86. Props2 = [validate_property(X, Modules) || X <- Props1],
  87. Struct1 = {application, AppName, Props2},
  88. file:write_file(AppFile, io_lib:format("~p.~n", [Struct1]))
  89. end
  90. end.
  91. erl_files(Dir) ->
  92. filelib:fold_files(Dir, ".erl", true, fun(F, Acc) -> [F|Acc] end, []).
  93. app_src_files(Dir) ->
  94. filelib:fold_files(Dir, ".app.src", true, fun(F, Acc) -> [F|Acc] end, []).
  95. is_app_src(Filename) ->
  96. Filename =/= filename:rootname(Filename, ".app.src").
  97. app_src_to_app(Filename) ->
  98. filename:basename(Filename, ".app.src") ++ ".app".
  99. erl_to_beam(EbinDir, Filename) ->
  100. filename:join(EbinDir, filename:basename(Filename, ".erl") ++ ".beam").
  101. is_compiled(EbinDir, ErlFile) ->
  102. BeamFile = erl_to_beam(EbinDir, ErlFile),
  103. mad_utils:last_modified(BeamFile) > mad_utils:last_modified(ErlFile).
  104. add_modules_property(Properties) ->
  105. case lists:keyfind(modules, 1, Properties) of
  106. {modules, _} ->
  107. Properties;
  108. _ ->
  109. Properties ++ [{modules, []}]
  110. end.
  111. sort(Files) ->
  112. sort_by_priority(Files, [], [], []).
  113. sort_by_priority([], High, Medium, Low) ->
  114. (High ++ Medium) ++ Low;
  115. sort_by_priority([H|T], High, Medium, Low) ->
  116. {High1, Medium1, Low1} =
  117. case mad_utils:exec("sed", ["-n", "'/-callback/p'", H]) of
  118. [] ->
  119. {High, [H|Medium], Low};
  120. _ ->
  121. {[H|High], Medium, Low}
  122. end,
  123. {High2, Medium2, Low2} =
  124. case mad_utils:exec("sed", ["-n", "'/-compile/p'", H]) of
  125. [] ->
  126. {High1, Medium1, Low1};
  127. _ ->
  128. {High1 -- [H], Medium1 -- [H], [H|Low1]}
  129. end,
  130. sort_by_priority(T, High2, Medium2, Low2).