Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

364 строки
14 KiB

10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
5 лет назад
5 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
  1. %%% @doc utility functions for directory and path handling of all kind.
  2. -module(rebar_dir).
  3. -export([base_dir/1,
  4. profile_dir/2,
  5. profile_dir_name/1,
  6. deps_dir/1,
  7. deps_dir/2,
  8. root_dir/1,
  9. checkouts_dir/1,
  10. checkouts_dir/2,
  11. plugins_dir/1,
  12. lib_dirs/1,
  13. home_dir/0,
  14. global_config_dir/1,
  15. global_config/1,
  16. global_config/0,
  17. global_cache_dir/1,
  18. local_cache_dir/1,
  19. get_cwd/0,
  20. template_globals/1,
  21. template_dir/1,
  22. processing_base_dir/1,
  23. processing_base_dir/2,
  24. make_relative_path/2,
  25. src_dirs/1, src_dirs/2,
  26. src_dir_opts/2, recursive/2,
  27. extra_src_dirs/1, extra_src_dirs/2,
  28. all_src_dirs/1, all_src_dirs/3,
  29. retarget_path/2,
  30. format_source_file_name/2]).
  31. -include("rebar.hrl").
  32. %% @doc returns the directory root for build artifacts
  33. %% for the current profile, such as `_build/default/'.
  34. -spec base_dir(rebar_state:t()) -> file:filename_all().
  35. base_dir(State) ->
  36. profile_dir(rebar_state:opts(State), rebar_state:current_profiles(State)).
  37. %% @doc returns the directory root for build artifacts for a given set
  38. %% of profiles.
  39. -spec profile_dir(rebar_dict(), [atom(), ...]) -> file:filename_all().
  40. profile_dir(Opts, Profiles) ->
  41. BasePath =
  42. case Profiles of
  43. [global | _] -> ?MODULE:global_cache_dir(Opts);
  44. [_|_] -> rebar_opts:get(Opts, base_dir, ?DEFAULT_BASE_DIR)
  45. end,
  46. DirName = profile_dir_name_(Profiles),
  47. filename:join(BasePath, DirName).
  48. %% @doc returns the directory name for build artifacts for a given set
  49. %% of profiles.
  50. -spec profile_dir_name(rebar_state:t()) -> file:filename_all().
  51. profile_dir_name(State) ->
  52. profile_dir_name_(rebar_state:current_profiles(State)).
  53. -spec profile_dir_name_([atom(), ...]) -> file:filename_all().
  54. profile_dir_name_(Profiles)
  55. when is_list(Profiles) ->
  56. case [rebar_utils:to_list(P) || P <- Profiles] of
  57. ["global" | _] -> "";
  58. ["bootstrap", "default"] -> "default";
  59. ["default"] -> "default";
  60. %% drop `default' from the profile dir if it's implicit and reverse order
  61. %% of profiles to match order passed to `as`
  62. ["default"|NonDefaultNames] -> rebar_string:join(NonDefaultNames, "+")
  63. end.
  64. %% @doc returns the directory where dependencies should be placed
  65. %% given the current profile.
  66. -spec deps_dir(rebar_state:t()) -> file:filename_all().
  67. deps_dir(State) ->
  68. filename:join(base_dir(State), rebar_state:get(State, deps_dir, ?DEFAULT_DEPS_DIR)).
  69. %% @doc returns the directory where a dependency should be placed
  70. %% given the current profile, based on its app name. Expects to be passed
  71. %% the result of `deps_dir/1' as a first argument.
  72. -spec deps_dir(file:filename_all(), file:filename_all()) -> file:filename_all().
  73. deps_dir(DepsDir, App) ->
  74. filename:join(DepsDir, App).
  75. %% @doc returns the absolute path for the project root (by default,
  76. %% the current working directory for the currently running escript).
  77. root_dir(State) ->
  78. filename:absname(rebar_state:get(State, root_dir, ?DEFAULT_ROOT_DIR)).
  79. %% @doc returns the expected location of the `_checkouts' directory.
  80. -spec checkouts_dir(rebar_state:t()) -> file:filename_all().
  81. checkouts_dir(State) ->
  82. rebar_file_utils:canonical_path(filename:join(root_dir(State), rebar_state:get(State, checkouts_dir, ?DEFAULT_CHECKOUTS_DIR))).
  83. %% @doc returns the expected location of a given app in the checkouts
  84. %% directory for the project.
  85. -spec checkouts_dir(rebar_state:t(), file:filename_all()) -> file:filename_all().
  86. checkouts_dir(State, App) ->
  87. filename:join(checkouts_dir(State), App).
  88. %% @doc Returns the directory where plugins are located.
  89. -spec plugins_dir(rebar_state:t()) -> file:filename_all().
  90. plugins_dir(State) ->
  91. case lists:member(global, rebar_state:current_profiles(State)) of
  92. true ->
  93. filename:join([base_dir(State), global_config_dir(State), rebar_state:get(State, plugins_dir, ?DEFAULT_PLUGINS_DIR)]);
  94. false ->
  95. filename:join(base_dir(State), rebar_state:get(State, plugins_dir, ?DEFAULT_PLUGINS_DIR))
  96. end.
  97. %% @doc returns the list of relative path where the project applications can
  98. %% be located.
  99. -spec lib_dirs(rebar_state:t()) -> file:filename_all().
  100. lib_dirs(State) ->
  101. rebar_state:get(State, project_app_dirs, ?DEFAULT_PROJECT_APP_DIRS).
  102. %% @doc returns the user's home directory.
  103. -spec home_dir() -> file:filename_all().
  104. home_dir() ->
  105. {ok, [[Home]]} = init:get_argument(home),
  106. Home.
  107. %% @doc returns the directory where the global configuration files for rebar3
  108. %% may be stored.
  109. -spec global_config_dir(rebar_state:t()) -> file:filename_all().
  110. global_config_dir(State) ->
  111. filename:join([rebar_config_dir(State), ".config", "rebar3"]).
  112. rebar_config_dir(State) ->
  113. case os:getenv("REBAR_GLOBAL_CONFIG_DIR") of
  114. false ->
  115. rebar_state:get(State, global_rebar_dir, home_dir());
  116. ConfDir ->
  117. ConfDir
  118. end.
  119. %% @doc returns the path of the global rebar.config file
  120. -spec global_config(rebar_state:t()) -> file:filename_all().
  121. global_config(State) ->
  122. filename:join(global_config_dir(State), "rebar.config").
  123. %% @doc returns the default path of the global rebar.config file
  124. -spec global_config() -> file:filename_all().
  125. global_config() ->
  126. Home = home_dir(),
  127. filename:join([Home, ".config", "rebar3", "rebar.config"]).
  128. %% @doc returns the location for the global cache directory
  129. -spec global_cache_dir(rebar_dict()) -> file:filename_all().
  130. global_cache_dir(Opts) ->
  131. Home = home_dir(),
  132. rebar_opts:get(Opts, global_rebar_dir, filename:join([Home, ".cache", "rebar3"])).
  133. %% @doc appends the cache directory to the path passed to this function.
  134. -spec local_cache_dir(file:filename_all()) -> file:filename_all().
  135. local_cache_dir(Dir) ->
  136. filename:join(Dir, ".rebar3").
  137. %% @doc returns the current working directory, with some specific
  138. %% conversions and handling done to be cross-platform compatible.
  139. -spec get_cwd() -> file:filename_all().
  140. get_cwd() ->
  141. {ok, Dir} = file:get_cwd(),
  142. %% On windows cwd may return capital letter for drive,
  143. %% for example C:/foobar. But as said in http://www.erlang.org/doc/man/filename.html#join-1
  144. %% filename:join/1,2 anyway will convert drive-letter to lowercase, so we have to "internalize"
  145. %% cwd as soon as it possible.
  146. filename:join([Dir]).
  147. %% @doc returns the file location for the global template
  148. %% configuration variables file.
  149. -spec template_globals(rebar_state:t()) -> file:filename_all().
  150. template_globals(State) ->
  151. filename:join([global_config_dir(State), "templates", "globals"]).
  152. %% @doc returns the location for the global template directory
  153. -spec template_dir(rebar_state:t()) -> file:filename_all().
  154. template_dir(State) ->
  155. filename:join([global_config_dir(State), "templates"]).
  156. %% @doc checks if the current working directory is the base directory
  157. %% for the project.
  158. -spec processing_base_dir(rebar_state:t()) -> boolean().
  159. processing_base_dir(State) ->
  160. Cwd = get_cwd(),
  161. processing_base_dir(State, Cwd).
  162. %% @doc checks if the passed in directory is the base directory for
  163. %% the project.
  164. -spec processing_base_dir(rebar_state:t(), file:filename()) -> boolean().
  165. processing_base_dir(State, Dir) ->
  166. AbsDir = filename:absname(Dir),
  167. AbsDir =:= rebar_state:get(State, base_dir).
  168. %% @doc take a source and a target path, and relativize the target path
  169. %% onto the source.
  170. %%
  171. %% Example:
  172. %% ```
  173. %% 1> rebar_dir:make_relative_path("a/b/c/d/file", "a/b/file").
  174. %% "c/d/file"
  175. %% 2> rebar_dir:make_relative_path("a/b/file", "a/b/c/d/file").
  176. %% "../../file"
  177. %% '''
  178. -spec make_relative_path(file:filename(), file:filename()) -> file:filename().
  179. make_relative_path(Source, Target) ->
  180. AbsSource = rebar_file_utils:normalized_path(Source),
  181. AbsTarget = rebar_file_utils:normalized_path(Target),
  182. do_make_relative_path(filename:split(AbsSource), filename:split(AbsTarget)).
  183. %% @private based on fragments of paths, replace the number of common
  184. %% segments by `../' bits, and add the rest of the source alone after it
  185. -spec do_make_relative_path([string()], [string()]) -> file:filename().
  186. do_make_relative_path([H|T1], [H|T2]) ->
  187. do_make_relative_path(T1, T2);
  188. do_make_relative_path(Source, Target) ->
  189. Base = lists:duplicate(max(length(Target) - 1, 0), ".."),
  190. filename:join(Base ++ Source).
  191. %%% @doc
  192. %%% `src_dirs' and `extra_src_dirs' can be configured with options
  193. %%% like this:
  194. %%% ```
  195. %%% {src_dirs,[{"foo",[{recursive,false}]}]}
  196. %%% {extra_src_dirs,[{"bar",[recursive]}]} (equivalent to {recursive,true})
  197. %%% '''
  198. %%% `src_dirs/1,2' and `extra_src_dirs/1,2' return only the list of
  199. %%% directories for the `src_dirs' and `extra_src_dirs' options
  200. %%% respectively, while `src_dirs_opts/2' returns the options list for
  201. %%% the given directory, no matter if it is configured as `src_dirs' or
  202. %%% `extra_src_dirs'.
  203. -spec src_dirs(rebar_dict()) -> list(file:filename_all()).
  204. src_dirs(Opts) -> src_dirs(Opts, []).
  205. %% @doc same as `src_dirs/1', but allows to pass in a list of default options.
  206. -spec src_dirs(rebar_dict(), list(file:filename_all())) -> list(file:filename_all()).
  207. src_dirs(Opts, Default) ->
  208. src_dirs(src_dirs, Opts, Default).
  209. %% @doc same as `src_dirs/1', but for the `extra_src_dirs' options
  210. -spec extra_src_dirs(rebar_dict()) -> list(file:filename_all()).
  211. extra_src_dirs(Opts) -> extra_src_dirs(Opts, []).
  212. %% @doc same as `src_dirs/2', but for the `extra_src_dirs' options
  213. -spec extra_src_dirs(rebar_dict(), list(file:filename_all())) -> list(file:filename_all()).
  214. extra_src_dirs(Opts, Default) ->
  215. src_dirs(extra_src_dirs, Opts, Default).
  216. %% @private agnostic version of src_dirs and extra_src_dirs.
  217. src_dirs(Type, Opts, Default) ->
  218. lists:usort([
  219. case D0 of
  220. {D,_} -> rebar_file_utils:normalize_relative_path(D);
  221. _ -> rebar_file_utils:normalize_relative_path(D0)
  222. end || D0 <- raw_src_dirs(Type,Opts,Default)]).
  223. %% @private extracts the un-formatted src_dirs or extra_src_dirs
  224. %% options as configured.
  225. raw_src_dirs(Type, Opts, Default) ->
  226. ErlOpts = rebar_opts:erl_opts(Opts),
  227. Vs = proplists:get_all_values(Type, ErlOpts),
  228. case lists:append([rebar_opts:get(Opts, Type, []) | Vs]) of
  229. [] -> Default;
  230. Dirs -> Dirs
  231. end.
  232. %% @doc returns all the source directories (`src_dirs' and
  233. %% `extra_src_dirs').
  234. -spec all_src_dirs(rebar_dict()) -> list(file:filename_all()).
  235. all_src_dirs(Opts) -> all_src_dirs(Opts, [], []).
  236. %% @doc returns all the source directories (`src_dirs' and
  237. %% `extra_src_dirs') while being able to configure defaults for both.
  238. -spec all_src_dirs(rebar_dict(), list(file:filename_all()), list(file:filename_all())) ->
  239. list(file:filename_all()).
  240. all_src_dirs(Opts, SrcDefault, ExtraDefault) ->
  241. lists:usort(src_dirs(Opts, SrcDefault) ++ extra_src_dirs(Opts, ExtraDefault)).
  242. %%% @doc
  243. %%% Return the list of options for the given src directory
  244. %%% If the same option is given multiple times for a directory in the
  245. %%% config, the priority order is: first occurence of `src_dirs'
  246. %%% followed by first occurence of `extra_src_dirs'.
  247. -spec src_dir_opts(rebar_dict(), file:filename_all()) -> [{atom(),term()}].
  248. src_dir_opts(Opts, Dir) ->
  249. RawSrcDirs = raw_src_dirs(src_dirs, Opts, []),
  250. RawExtraSrcDirs = raw_src_dirs(extra_src_dirs, Opts, []),
  251. AllOpts = [Opt || {D, Opt} <- RawSrcDirs++RawExtraSrcDirs, D==Dir],
  252. lists:ukeysort(1, proplists:unfold(lists:append(AllOpts))).
  253. %%% @doc
  254. %%% Return the value of the 'recursive' option for the given directory.
  255. %%% If not given, the value of 'recursive' in the 'erlc_compiler'
  256. %%% options is used, and finally the default is 'true'.
  257. -spec recursive(rebar_dict(), file:filename_all()) -> boolean().
  258. recursive(Opts, Dir) ->
  259. DirOpts = src_dir_opts(Opts, Dir),
  260. Default = proplists:get_value(recursive,
  261. rebar_opts:get(Opts, erlc_compiler, []),
  262. true),
  263. R = proplists:get_value(recursive, DirOpts, Default),
  264. R.
  265. %% @doc given a path if that path is an ancestor of an app dir, return the path relative to that
  266. %% apps outdir. If the path is not an ancestor to any app dirs but is an ancestor of the
  267. %% project root, return the path relative to the project base_dir. If it is not an ancestor
  268. %% of either return it unmodified
  269. -spec retarget_path(rebar_state:t(), string()) -> string().
  270. retarget_path(State, Path) ->
  271. ProjectApps = rebar_state:project_apps(State),
  272. retarget_path(State, Path, ProjectApps).
  273. %% @private worker for retarget_path/2
  274. %% @end
  275. %% not relative to any apps in project, check to see it's relative to
  276. %% project root
  277. retarget_path(State, Path, []) ->
  278. case rebar_file_utils:path_from_ancestor(rebar_file_utils:canonical_path(Path), rebar_state:dir(State)) of
  279. {ok, NewPath} -> filename:join([base_dir(State), NewPath]);
  280. %% not relative to project root, don't modify
  281. {error, badparent} -> Path
  282. end;
  283. %% relative to current app, retarget to the same dir relative to
  284. %% the app's out_dir
  285. retarget_path(State, Path, [App|Rest]) ->
  286. case rebar_file_utils:path_from_ancestor(rebar_file_utils:canonical_path(Path), rebar_app_info:dir(App)) of
  287. {ok, NewPath} -> filename:join([rebar_app_info:out_dir(App), NewPath]);
  288. {error, badparent} -> retarget_path(State, Path, Rest)
  289. end.
  290. format_source_file_name(Path, Opts) ->
  291. Type = case rebar_opts:get(Opts, compiler_source_format,
  292. ?DEFAULT_COMPILER_SOURCE_FORMAT) of
  293. V when V == absolute; V == relative; V == build ->
  294. V;
  295. Other ->
  296. warn_source_format_once(Other)
  297. end,
  298. case Type of
  299. absolute -> resolve_linked_source(Path);
  300. build -> Path;
  301. relative ->
  302. Cwd = rebar_dir:get_cwd(),
  303. rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd)
  304. end.
  305. %% @private displays a warning for the compiler source format option
  306. %% only once
  307. -spec warn_source_format_once(term()) -> ok.
  308. warn_source_format_once(Format) ->
  309. Warn = application:get_env(rebar, warn_source_format) =/= {ok, false},
  310. application:set_env(rebar, warn_source_format, false),
  311. case Warn of
  312. false ->
  313. ok;
  314. true ->
  315. ?WARN("Invalid argument ~p for compiler_source_format - "
  316. "assuming ~ts~n", [Format, ?DEFAULT_COMPILER_SOURCE_FORMAT])
  317. end.
  318. %% @private takes a filename and canonicalizes its path if it is a link.
  319. -spec resolve_linked_source(file:filename()) -> file:filename().
  320. resolve_linked_source(Src) ->
  321. {Dir, Base} = rebar_file_utils:split_dirname(Src),
  322. filename:join(rebar_file_utils:resolve_link(Dir), Base).