about summary refs log tree commit diff
path: root/pkgs/development/beam-modules/mix-bootstrap
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/development/beam-modules/mix-bootstrap')
-rwxr-xr-xpkgs/development/beam-modules/mix-bootstrap112
1 files changed, 112 insertions, 0 deletions
diff --git a/pkgs/development/beam-modules/mix-bootstrap b/pkgs/development/beam-modules/mix-bootstrap
new file mode 100755
index 000000000000..c4a1b364daa7
--- /dev/null
+++ b/pkgs/development/beam-modules/mix-bootstrap
@@ -0,0 +1,112 @@
+#!/usr/bin/env escript
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%%! -smp enable
+%%% ---------------------------------------------------------------------------
+%%% @doc
+%%% The purpose of this command is to prepare a rebar3 project so that
+%%% rebar3 understands that the dependencies are all already
+%%% installed. If you want a hygienic build on nix then you must run
+%%% this command before running rebar3. I suggest that you add a
+%%% `Makefile` to your project and have the bootstrap command be a
+%%% dependency of the build commands. See the nix documentation for
+%%% more information.
+%%%
+%%% This command designed to have as few dependencies as possible so
+%%% that it can be a dependency of root level packages like rebar3. To
+%%% that end it does many things in a fairly simplistic way. That is
+%%% by design.
+%%%
+%%% ### Assumptions
+%%%
+%%% This command makes the following assumptions:
+%%%
+%%% * It is run in a nix-shell or nix-build environment
+%%% * that all dependencies have been added to the ERL_LIBS
+%%%   Environment Variable
+
+-record(data, {version
+              , erl_libs
+              , root
+              , name
+              , registry_snapshot}).
+-define(LOCAL_HEX_REGISTRY, "registry.ets").
+
+main(Args) ->
+    {ok, RequiredData} = gather_required_data_from_the_environment(Args),
+    ok = bootstrap_libs(RequiredData).
+
+%% @doc
+%% This takes an app name in the standard OTP <name>-<version> format
+%% and returns just the app name. Why? because rebar is doesn't
+%% respect OTP conventions in some cases.
+-spec fixup_app_name(file:name()) -> string().
+fixup_app_name(Path) ->
+    BaseName = filename:basename(Path),
+    case string:tokens(BaseName, "-") of
+        [Name, _Version] -> Name;
+        Name -> Name
+    end.
+
+
+-spec gather_required_data_from_the_environment([string()]) -> {ok, #data{}}.
+gather_required_data_from_the_environment(_) ->
+    {ok, #data{ version = guard_env("version")
+              , erl_libs = os:getenv("ERL_LIBS", [])
+              , root = code:root_dir()
+              , name = guard_env("name")
+              , registry_snapshot = guard_env("HEX_REGISTRY_SNAPSHOT")}}.
+
+-spec guard_env(string()) -> string().
+guard_env(Name) ->
+    case os:getenv(Name) of
+        false ->
+            stderr("Expected Environment variable ~s! Are you sure you are "
+                   "running in a Nix environment? Either a nix-build, "
+                   "nix-shell, etc?~n", [Name]),
+            erlang:halt(1);
+        Variable ->
+            Variable
+    end.
+
+-spec bootstrap_libs(#data{}) -> ok.
+bootstrap_libs(#data{erl_libs = ErlLibs}) ->
+    io:format("Bootstrapping dependent libraries~n"),
+    Target = "_build/prod/lib/",
+    Paths = string:tokens(ErlLibs, ":"),
+    CopiableFiles =
+        lists:foldl(fun(Path, Acc) ->
+                            gather_directory_contents(Path) ++ Acc
+                    end, [], Paths),
+    lists:foreach(fun (Path) ->
+                          ok = link_app(Path, Target)
+                  end, CopiableFiles).
+
+-spec gather_directory_contents(string()) -> [{string(), string()}].
+gather_directory_contents(Path) ->
+    {ok, Names} = file:list_dir(Path),
+    lists:map(fun(AppName) ->
+                 {filename:join(Path, AppName), fixup_app_name(AppName)}
+              end, Names).
+
+%% @doc
+%% Makes a symlink from the directory pointed at by Path to a
+%% directory of the same name in Target. So if we had a Path of
+%% {`foo/bar/baz/bash`, `baz`} and a Target of `faz/foo/foos`, the symlink
+%% would be `faz/foo/foos/baz`.
+-spec link_app({string(), string()}, string()) -> ok.
+link_app({Path, TargetFile}, TargetDir) ->
+    Target = filename:join(TargetDir, TargetFile),
+    ok = make_symlink(Path, Target).
+
+-spec make_symlink(string(), string()) -> ok.
+make_symlink(Path, TargetFile) ->
+    file:delete(TargetFile),
+    ok = filelib:ensure_dir(TargetFile),
+    io:format("Making symlink from ~s to ~s~n", [Path, TargetFile]),
+    ok = file:make_symlink(Path, TargetFile).
+
+%% @doc
+%% Write the result of the format string out to stderr.
+-spec stderr(string(), [term()]) -> ok.
+stderr(FormatStr, Args) ->
+    io:put_chars(standard_error, io_lib:format(FormatStr, Args)).