mad_port.erl 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. -module(mad_port).
  2. -copyright('Maxim Sokhatsky').
  3. -compile(export_all).
  4. compile(Dir,Config) ->
  5. case mad_utils:get_value(port_specs, Config, []) of
  6. [] -> [false];
  7. X -> compile_port(Dir,X,Config) end.
  8. compile_port(Dir,Specs0,Config) ->
  9. {_,Flavour} = os:type(),
  10. System = atom_to_list(Flavour),
  11. Specs = [ {O,F} || {Sys,O,F} <- Specs0, Sys == System] ++
  12. [ {O,F} || {O,F} <- Specs0],
  13. filelib:ensure_dir(Dir ++ "/priv/"),
  14. Env = [ {Var,Val} || {Sys,Var,Val} <- mad_utils:get_value(port_env, Config, []), system(Sys,System) ] ++
  15. [ {Var,Val} || {Var,Val} <- mad_utils:get_value(port_env, Config, []) ],
  16. Job = fun({Target, Patterns}) ->
  17. Files = files(Dir,Patterns),
  18. LinkLang = link_lang(Files),
  19. TargetType = target_type(extension(Target)),
  20. Compile = fun(F) ->
  21. Obj = to_obj(F),
  22. case is_compiled(Obj,F) of
  23. false ->
  24. Ext = extension(F),
  25. CC = compiler(Ext),
  26. TplCC = tpl_cc(TargetType,CC),
  27. Env1 = [{"PORT_IN_FILES", F},{"PORT_OUT_FILE", Obj}] ++ Env ++ default_env(),
  28. CmdCC = string:strip(expand(System,TplCC,Env1)),
  29. Cmd = expand(System,CmdCC,[{"CXXFLAGS",""},{"LDFLAGS",""},{"CFLAGS",""}]),
  30. {_,Status,Report} = sh:run("cc",string:tokens(Cmd," "),binary,Dir,Env),
  31. case Status of
  32. 0 -> {ok,Obj};
  33. _ -> {error, "Port Compilation Error:~n" ++ io_lib:format("~ts",[Report])},true
  34. end;
  35. true -> {ok,Obj}
  36. end
  37. end,
  38. Res = lists:foldl(fun({ok,X},Acc) -> [X|Acc];
  39. ({error,Err},_)-> {error,Err}
  40. end,[],[Compile(F) || F <- Files]),
  41. case Res of
  42. {error, _}=Err -> Err;
  43. Objs -> Env2 = [{"PORT_IN_FILES", string:join(Objs," ")},
  44. {"PORT_OUT_FILE", Target}] ++ Env ++ default_env(),
  45. TplLD = tpl_ld(TargetType,LinkLang),
  46. CmdLD = string:strip(expand(System,TplLD,Env2)),
  47. Cmd = expand(System,CmdLD,[{"CXXFLAGS",""},{"LDFLAGS",""},{"CFLAGS",""}]),
  48. {_,Status,Report} = sh:run("cc",string:tokens(Cmd," "),binary,Dir,Env),
  49. case Status of
  50. 0 -> false;
  51. _ -> mad:info("Port Compilation Error:~n" ++ io_lib:format("~ts",[Report]),[]),
  52. {error, Report},true
  53. end
  54. end
  55. end,
  56. [Job(S)||S<-Specs].
  57. to_obj(F) -> filename:rootname(F) ++ ".o".
  58. %%FIXME
  59. expand(System, String, []) -> String;
  60. expand(System, String, [{K,V}|Env]) ->
  61. New = re:replace(String, io_lib:format("\\${?(~s)}?",[K]), V, [global, {return, list}]),
  62. expand(System,New,Env);
  63. expand(System, String, [{Sys,K,V}|Env]) ->
  64. case system(Sys,System) of
  65. true -> New = re:replace(String, io_lib:format("\\${?(~s)}?",[K]), V, [global, {return, list}]),
  66. expand(System,New,Env);
  67. false -> expand(System,String,Env)
  68. end.
  69. extension(F) -> filename:extension(F).
  70. is_compiled(O,F) -> filelib:is_file(O) andalso (mad_utils:last_modified(O) >= mad_utils:last_modified(F)).
  71. join(A,B) -> filename:join(A,B).
  72. concat(X) -> lists:concat(X).
  73. system(Sys,System) -> Sys == System orelse match(Sys,System).
  74. match(Re,System) -> case re:run(System, Re, [{capture,none}]) of match -> true; nomatch -> false end.
  75. erts_dir() -> join(code:root_dir(), concat(["erts-", erlang:system_info(version)])) .
  76. erts_dir(include) -> " -I"++join(erts_dir(), "include").
  77. ei_dir() -> case code:lib_dir(erl_interface) of {error,bad_name} -> ""; E -> E end.
  78. ei_dir(include) -> case ei_dir() of "" -> ""; E -> " -I"++join(E,"include") end;
  79. ei_dir(lib) -> case ei_dir() of "" -> ""; E -> " -L"++join(E,"lib") end.
  80. link_lang(Files) -> lists:foldl(fun(F,cxx) -> cxx;
  81. (F,cc) -> case compiler(extension(F)) == "$CXX" of
  82. true -> cxx;false -> cc end
  83. end,cc,Files).
  84. files(Dir,Patterns)-> files(Dir,Patterns, []).
  85. files(_,[], Acc) -> lists:reverse(Acc);
  86. files(D,[H|T], Acc) ->
  87. files(D,T,filelib:wildcard(join(D,H))++Acc).
  88. target_type(".so") -> drv;
  89. target_type(".dll") -> drv;
  90. target_type("") -> exe;
  91. target_type(".exe") -> exe.
  92. tpl_cc(drv,"$CC") -> " -c $CFLAGS $DRV_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE";
  93. tpl_cc(drv,"$CXX")-> " -c $CXXFLAGS $DRV_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE";
  94. tpl_cc(exe,"$CC") -> " -c $CFLAGS $EXE_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE";
  95. tpl_cc(exe,"$CXX")-> " -c $CXXFLAGS $EXE_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE".
  96. tpl_ld(drv,cc) -> " $PORT_IN_FILES $LDFLAGS $DRV_LDFLAGS -o $PORT_OUT_FILE";
  97. tpl_ld(drv,cxx) -> " $PORT_IN_FILES $LDFLAGS $DRV_LDFLAGS -o $PORT_OUT_FILE";
  98. tpl_ld(exe,cc) -> " $PORT_IN_FILES $LDFLAGS $EXE_LDFLAGS -o $PORT_OUT_FILE";
  99. tpl_ld(exe,cxx) -> " $PORT_IN_FILES $LDFLAGS $EXE_LDFLAGS -o $PORT_OUT_FILE".
  100. erl_ldflag() -> concat([ei_dir(include), erts_dir(include), " "]).
  101. default_env() ->
  102. Arch = os:getenv("REBAR_TARGET_ARCH"),
  103. Vsn = os:getenv("REBAR_TARGET_ARCH_VSN"),
  104. [
  105. {"darwin", "DRV_LDFLAGS", "-bundle -flat_namespace -undefined suppress " ++ei_dir(lib) ++" -lerl_interface -lei"},
  106. {"DRV_CFLAGS" , "-g -Wall -fPIC -MMD " ++ erl_ldflag()},
  107. {"DRV_LDFLAGS", "-shared " ++ erl_ldflag()},
  108. {"EXE_CFLAGS" , "-g -Wall -fPIC -MMD " ++ erl_ldflag()},
  109. {"EXE_LDFLAGS", ei_dir(lib)++" -lerl_interface -lei"},
  110. {"ERL_EI_LIBDIR", ei_dir(lib)}
  111. ].
  112. compiler(".cc") -> "$CXX";
  113. compiler(".cp") -> "$CXX";
  114. compiler(".cxx") -> "$CXX";
  115. compiler(".cpp") -> "$CXX";
  116. compiler(".CPP") -> "$CXX";
  117. compiler(".c++") -> "$CXX";
  118. compiler(".C") -> "$CXX";
  119. compiler(cxx) -> "$CXX";
  120. compiler(cc) -> "$CC";
  121. compiler(_) -> "$CC".