|
|
- %%% @doc utility functions for directory and path handling of all kind.
- -module(rebar_dir).
-
- -export([base_dir/1,
- profile_dir/2,
- profile_dir_name/1,
- deps_dir/1,
- deps_dir/2,
- root_dir/1,
- checkouts_dir/1,
- checkouts_dir/2,
- checkouts_out_dir/1,
- checkouts_out_dir/2,
- plugins_dir/1,
- lib_dirs/1,
- home_dir/0,
- global_config_dir/1,
- global_config/1,
- global_config/0,
- global_cache_dir/1,
- local_cache_dir/1,
- get_cwd/0,
- template_globals/1,
- template_dir/1,
- processing_base_dir/1,
- processing_base_dir/2,
- make_relative_path/2,
- src_dirs/1, src_dirs/2,
- src_dir_opts/2, recursive/2,
- extra_src_dirs/1, extra_src_dirs/2,
- all_src_dirs/1, all_src_dirs/3,
- retarget_path/2,
- format_source_file_name/1,
- format_source_file_name/2]).
-
- -include("rebar.hrl").
-
- %% @doc returns the directory root for build artifacts
- %% for the current profile, such as `_build/default/'.
- -spec base_dir(rebar_state:t()) -> file:filename_all().
- base_dir(State) ->
- profile_dir(rebar_state:opts(State), rebar_state:current_profiles(State)).
-
- %% @doc returns the directory root for build artifacts for a given set
- %% of profiles.
- -spec profile_dir(rebar_dict(), [atom(), ...]) -> file:filename_all().
- profile_dir(Opts, Profiles) ->
- BasePath =
- case Profiles of
- [global | _] -> ?MODULE:global_cache_dir(Opts);
- [_|_] -> rebar_opts:get(Opts, base_dir, ?DEFAULT_BASE_DIR)
- end,
- DirName = profile_dir_name_(Profiles),
- filename:join(BasePath, DirName).
-
- %% @doc returns the directory name for build artifacts for a given set
- %% of profiles.
- -spec profile_dir_name(rebar_state:t()) -> file:filename_all().
- profile_dir_name(State) ->
- profile_dir_name_(rebar_state:current_profiles(State)).
-
- -spec profile_dir_name_([atom(), ...]) -> file:filename_all().
- profile_dir_name_(Profiles)
- when is_list(Profiles) ->
- case [rebar_utils:to_list(P) || P <- Profiles] of
- ["global" | _] -> "";
- ["bootstrap", "default"] -> "default";
- ["default"] -> "default";
- %% drop `default' from the profile dir if it's implicit and reverse order
- %% of profiles to match order passed to `as`
- ["default"|NonDefaultNames] -> rebar_string:join(NonDefaultNames, "+")
- end.
-
- %% @doc returns the directory where dependencies should be placed
- %% given the current profile.
- -spec deps_dir(rebar_state:t()) -> file:filename_all().
- deps_dir(State) ->
- filename:join(base_dir(State), rebar_state:get(State, deps_dir, ?DEFAULT_DEPS_DIR)).
-
- %% @doc returns the directory where a dependency should be placed
- %% given the current profile, based on its app name. Expects to be passed
- %% the result of `deps_dir/1' as a first argument.
- -spec deps_dir(file:filename_all(), file:filename_all()) -> file:filename_all().
- deps_dir(DepsDir, App) ->
- filename:join(DepsDir, App).
-
- %% @doc returns the absolute path for the project root (by default,
- %% the current working directory for the currently running escript).
- root_dir(State) ->
- filename:absname(rebar_state:get(State, root_dir, ?DEFAULT_ROOT_DIR)).
-
- %% @doc returns the expected location of the `_checkouts' directory.
- -spec checkouts_dir(rebar_state:t()) -> file:filename_all().
- checkouts_dir(State) ->
- rebar_file_utils:canonical_path(filename:join(root_dir(State), rebar_state:get(State, checkouts_dir, ?DEFAULT_CHECKOUTS_DIR))).
-
- %% @doc returns the expected location of a given app in the checkouts
- %% directory for the project.
- -spec checkouts_dir(rebar_state:t(), file:filename_all()) -> file:filename_all().
- checkouts_dir(State, App) ->
- filename:join(checkouts_dir(State), App).
-
- %% @doc returns the location of the directory checkouts are built to
- -spec checkouts_out_dir(rebar_state:t()) -> file:filename_all().
- checkouts_out_dir(State) ->
- filename:join(base_dir(State), rebar_state:get(State, checkouts_out_dir, ?DEFAULT_CHECKOUTS_OUT_DIR)).
-
- %% @doc returns the expected location of a given app in the checkouts
- %% directory for the project.
- -spec checkouts_out_dir(rebar_state:t(), file:filename_all()) -> file:filename_all().
- checkouts_out_dir(State, App) ->
- filename:join(checkouts_out_dir(State), App).
-
- %% @doc Returns the directory where plugins are located.
- -spec plugins_dir(rebar_state:t()) -> file:filename_all().
- plugins_dir(State) ->
- case lists:member(global, rebar_state:current_profiles(State)) of
- true ->
- filename:join([base_dir(State), global_config_dir(State), rebar_state:get(State, plugins_dir, ?DEFAULT_PLUGINS_DIR)]);
- false ->
- filename:join(base_dir(State), rebar_state:get(State, plugins_dir, ?DEFAULT_PLUGINS_DIR))
- end.
-
- %% @doc returns the list of relative path where the project applications can
- %% be located.
- -spec lib_dirs(rebar_state:t()) -> file:filename_all().
- lib_dirs(State) ->
- rebar_state:get(State, project_app_dirs, ?DEFAULT_PROJECT_APP_DIRS).
-
- %% @doc returns the user's home directory.
- -spec home_dir() -> file:filename_all().
- home_dir() ->
- {ok, [[Home]]} = init:get_argument(home),
- Home.
-
- %% @doc returns the directory where the global configuration files for rebar3
- %% may be stored.
- -spec global_config_dir(rebar_state:t()) -> file:filename_all().
- global_config_dir(State) ->
- filename:join([rebar_config_dir(State), ".config", "rebar3"]).
-
- rebar_config_dir(State) ->
- case os:getenv("REBAR_GLOBAL_CONFIG_DIR") of
- false ->
- rebar_state:get(State, global_rebar_dir, home_dir());
- ConfDir ->
- ConfDir
- end.
-
- %% @doc returns the path of the global rebar.config file
- -spec global_config(rebar_state:t()) -> file:filename_all().
- global_config(State) ->
- filename:join(global_config_dir(State), "rebar.config").
-
- %% @doc returns the default path of the global rebar.config file
- -spec global_config() -> file:filename_all().
- global_config() ->
- Home = home_dir(),
- filename:join([Home, ".config", "rebar3", "rebar.config"]).
-
- %% @doc returns the location for the global cache directory
- -spec global_cache_dir(rebar_dict()) -> file:filename_all().
- global_cache_dir(Opts) ->
- Home = home_dir(),
- rebar_opts:get(Opts, global_rebar_dir, filename:join([Home, ".cache", "rebar3"])).
-
- %% @doc appends the cache directory to the path passed to this function.
- -spec local_cache_dir(file:filename_all()) -> file:filename_all().
- local_cache_dir(Dir) ->
- filename:join(Dir, ".rebar3").
-
- %% @doc returns the current working directory, with some specific
- %% conversions and handling done to be cross-platform compatible.
- -spec get_cwd() -> file:filename_all().
- get_cwd() ->
- {ok, Dir} = file:get_cwd(),
- %% On windows cwd may return capital letter for drive,
- %% for example C:/foobar. But as said in http://www.erlang.org/doc/man/filename.html#join-1
- %% filename:join/1,2 anyway will convert drive-letter to lowercase, so we have to "internalize"
- %% cwd as soon as it possible.
- filename:join([Dir]).
-
- %% @doc returns the file location for the global template
- %% configuration variables file.
- -spec template_globals(rebar_state:t()) -> file:filename_all().
- template_globals(State) ->
- filename:join([global_config_dir(State), "templates", "globals"]).
-
- %% @doc returns the location for the global template directory
- -spec template_dir(rebar_state:t()) -> file:filename_all().
- template_dir(State) ->
- filename:join([global_config_dir(State), "templates"]).
-
- %% @doc checks if the current working directory is the base directory
- %% for the project.
- -spec processing_base_dir(rebar_state:t()) -> boolean().
- processing_base_dir(State) ->
- Cwd = get_cwd(),
- processing_base_dir(State, Cwd).
-
- %% @doc checks if the passed in directory is the base directory for
- %% the project.
- -spec processing_base_dir(rebar_state:t(), file:filename()) -> boolean().
- processing_base_dir(State, Dir) ->
- AbsDir = filename:absname(Dir),
- AbsDir =:= rebar_state:get(State, base_dir).
-
- %% @doc take a source and a target path, and relativize the target path
- %% onto the source.
- %%
- %% Example:
- %% ```
- %% 1> rebar_dir:make_relative_path("a/b/c/d/file", "a/b/file").
- %% "c/d/file"
- %% 2> rebar_dir:make_relative_path("a/b/file", "a/b/c/d/file").
- %% "../../file"
- %% '''
- -spec make_relative_path(file:filename(), file:filename()) -> file:filename().
- make_relative_path(Source, Target) ->
- AbsSource = rebar_file_utils:normalized_path(Source),
- AbsTarget = rebar_file_utils:normalized_path(Target),
- do_make_relative_path(filename:split(AbsSource), filename:split(AbsTarget)).
-
- %% @private based on fragments of paths, replace the number of common
- %% segments by `../' bits, and add the rest of the source alone after it
- -spec do_make_relative_path([string()], [string()]) -> file:filename().
- do_make_relative_path([H|T1], [H|T2]) ->
- do_make_relative_path(T1, T2);
- do_make_relative_path(Source, Target) ->
- Base = lists:duplicate(max(length(Target) - 1, 0), ".."),
- filename:join(Base ++ Source).
-
- %%% @doc
- %%% `src_dirs' and `extra_src_dirs' can be configured with options
- %%% like this:
- %%% ```
- %%% {src_dirs,[{"foo",[{recursive,false}]}]}
- %%% {extra_src_dirs,[{"bar",[recursive]}]} (equivalent to {recursive,true})
- %%% '''
- %%% `src_dirs/1,2' and `extra_src_dirs/1,2' return only the list of
- %%% directories for the `src_dirs' and `extra_src_dirs' options
- %%% respectively, while `src_dirs_opts/2' returns the options list for
- %%% the given directory, no matter if it is configured as `src_dirs' or
- %%% `extra_src_dirs'.
- -spec src_dirs(rebar_dict()) -> list(file:filename_all()).
- src_dirs(Opts) -> src_dirs(Opts, []).
-
- %% @doc same as `src_dirs/1', but allows to pass in a list of default options.
- -spec src_dirs(rebar_dict(), list(file:filename_all())) -> list(file:filename_all()).
- src_dirs(Opts, Default) ->
- src_dirs(src_dirs, Opts, Default).
-
- %% @doc same as `src_dirs/1', but for the `extra_src_dirs' options
- -spec extra_src_dirs(rebar_dict()) -> list(file:filename_all()).
- extra_src_dirs(Opts) -> extra_src_dirs(Opts, []).
-
- %% @doc same as `src_dirs/2', but for the `extra_src_dirs' options
- -spec extra_src_dirs(rebar_dict(), list(file:filename_all())) -> list(file:filename_all()).
- extra_src_dirs(Opts, Default) ->
- src_dirs(extra_src_dirs, Opts, Default).
-
- %% @private agnostic version of src_dirs and extra_src_dirs.
- src_dirs(Type, Opts, Default) ->
- lists:usort([
- case D0 of
- {D,_} -> rebar_file_utils:normalize_relative_path(D);
- _ -> rebar_file_utils:normalize_relative_path(D0)
- end || D0 <- raw_src_dirs(Type,Opts,Default)]).
-
- %% @private extracts the un-formatted src_dirs or extra_src_dirs
- %% options as configured.
- raw_src_dirs(Type, Opts, Default) ->
- ErlOpts = rebar_opts:erl_opts(Opts),
- Vs = proplists:get_all_values(Type, ErlOpts),
- case lists:append([rebar_opts:get(Opts, Type, []) | Vs]) of
- [] -> Default;
- Dirs -> Dirs
- end.
-
- %% @doc returns all the source directories (`src_dirs' and
- %% `extra_src_dirs').
- -spec all_src_dirs(rebar_dict()) -> list(file:filename_all()).
- all_src_dirs(Opts) -> all_src_dirs(Opts, [], []).
-
- %% @doc returns all the source directories (`src_dirs' and
- %% `extra_src_dirs') while being able to configure defaults for both.
- -spec all_src_dirs(rebar_dict(), list(file:filename_all()), list(file:filename_all())) ->
- list(file:filename_all()).
- all_src_dirs(Opts, SrcDefault, ExtraDefault) ->
- lists:usort(src_dirs(Opts, SrcDefault) ++ extra_src_dirs(Opts, ExtraDefault)).
-
- %%% @doc
- %%% Return the list of options for the given src directory
- %%% If the same option is given multiple times for a directory in the
- %%% config, the priority order is: first occurence of `src_dirs'
- %%% followed by first occurence of `extra_src_dirs'.
- -spec src_dir_opts(rebar_dict(), file:filename_all()) -> [{atom(),term()}].
- src_dir_opts(Opts, Dir) ->
- RawSrcDirs = raw_src_dirs(src_dirs, Opts, []),
- RawExtraSrcDirs = raw_src_dirs(extra_src_dirs, Opts, []),
- AllOpts = [Opt || {D, Opt} <- RawSrcDirs++RawExtraSrcDirs, D==Dir],
- lists:ukeysort(1, proplists:unfold(lists:append(AllOpts))).
-
- %%% @doc
- %%% Return the value of the 'recursive' option for the given directory.
- %%% If not given, the value of 'recursive' in the 'erlc_compiler'
- %%% options is used, and finally the default is 'true'.
- -spec recursive(rebar_dict(), file:filename_all()) -> boolean().
- recursive(Opts, Dir) ->
- DirOpts = src_dir_opts(Opts, Dir),
- Default = proplists:get_value(recursive,
- rebar_opts:get(Opts, erlc_compiler, []),
- true),
- R = proplists:get_value(recursive, DirOpts, Default),
- R.
-
- %% @doc given a path if that path is an ancestor of an app dir, return the path relative to that
- %% apps outdir. If the path is not an ancestor to any app dirs but is an ancestor of the
- %% project root, return the path relative to the project base_dir. If it is not an ancestor
- %% of either return it unmodified
- -spec retarget_path(rebar_state:t(), string()) -> string().
- retarget_path(State, Path) ->
- ProjectApps = rebar_state:project_apps(State),
- retarget_path(State, Path, ProjectApps).
-
- %% @private worker for retarget_path/2
- %% @end
- %% not relative to any apps in project, check to see it's relative to
- %% project root
- retarget_path(State, Path, []) ->
- case rebar_file_utils:path_from_ancestor(rebar_file_utils:canonical_path(Path), rebar_state:dir(State)) of
- {ok, NewPath} -> filename:join([base_dir(State), NewPath]);
- %% not relative to project root, don't modify
- {error, badparent} -> Path
- end;
- %% relative to current app, retarget to the same dir relative to
- %% the app's out_dir
- retarget_path(State, Path, [App|Rest]) ->
- case rebar_file_utils:path_from_ancestor(rebar_file_utils:canonical_path(Path), rebar_app_info:dir(App)) of
- {ok, NewPath} -> filename:join([rebar_app_info:out_dir(App), NewPath]);
- {error, badparent} -> retarget_path(State, Path, Rest)
- end.
-
- format_source_file_name(Path) ->
- format_source_file_name(Path, dict:new()).
-
- format_source_file_name(Path, Opts) ->
- Type = case rebar_opts:get(Opts, compiler_source_format,
- ?DEFAULT_COMPILER_SOURCE_FORMAT) of
- V when V == absolute; V == relative; V == build ->
- V;
- Other ->
- warn_source_format_once(Other)
- end,
- case Type of
- absolute -> resolve_linked_source(Path);
- build -> Path;
- relative ->
- Cwd = rebar_dir:get_cwd(),
- rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd)
- end.
-
- %% @private displays a warning for the compiler source format option
- %% only once
- -spec warn_source_format_once(term()) -> ok.
- warn_source_format_once(Format) ->
- Warn = application:get_env(rebar, warn_source_format) =/= {ok, false},
- application:set_env(rebar, warn_source_format, false),
- case Warn of
- false ->
- ok;
- true ->
- ?WARN("Invalid argument ~p for compiler_source_format - "
- "assuming ~ts~n", [Format, ?DEFAULT_COMPILER_SOURCE_FORMAT])
- end.
-
- %% @private takes a filename and canonicalizes its path if it is a link.
- -spec resolve_linked_source(file:filename()) -> file:filename().
- resolve_linked_source(Src) ->
- {Dir, Base} = rebar_file_utils:split_dirname(Src),
- filename:join(rebar_file_utils:resolve_link(Dir), Base).
|