From 383626b9d7dd56618f0789eefee147a097ada4a4 Mon Sep 17 00:00:00 2001 From: Eric Merritt Date: Fri, 15 Apr 2016 11:06:57 -0700 Subject: [PATCH] move rebar3-nix-bootstrap to rebar3 This moves rebar3-nix-bootstrap from its own repository to rebar3. Its a single file and this vastly reduces the complexity of making changes. --- .../development/beam-modules/build-rebar3.nix | 2 +- .../tools/build-managers/rebar3/default.nix | 10 +- .../rebar3/rebar3-nix-bootstrap | 256 ++++++++++++++++++ .../erlang/rebar3-nix-bootstrap/default.nix | 24 -- pkgs/top-level/all-packages.nix | 1 - 5 files changed, 264 insertions(+), 29 deletions(-) create mode 100755 pkgs/development/tools/build-managers/rebar3/rebar3-nix-bootstrap delete mode 100644 pkgs/development/tools/erlang/rebar3-nix-bootstrap/default.nix diff --git a/pkgs/development/beam-modules/build-rebar3.nix b/pkgs/development/beam-modules/build-rebar3.nix index 2627ddf99a6b..f13322519fd8 100644 --- a/pkgs/development/beam-modules/build-rebar3.nix +++ b/pkgs/development/beam-modules/build-rebar3.nix @@ -48,7 +48,7 @@ let configurePhase = '' runHook preConfigure - rebar3-nix-bootstrap + ${erlang}/bin/escript ${rebar3.bootstrapper} runHook postConfigure ''; diff --git a/pkgs/development/tools/build-managers/rebar3/default.nix b/pkgs/development/tools/build-managers/rebar3/default.nix index 057ae59b1445..2b5eee407a44 100644 --- a/pkgs/development/tools/build-managers/rebar3/default.nix +++ b/pkgs/development/tools/build-managers/rebar3/default.nix @@ -1,10 +1,12 @@ { stdenv, writeText, callPackage, fetchurl, - fetchHex, erlang, hermeticRebar3 ? true, rebar3-nix-bootstrap, + fetchHex, erlang, hermeticRebar3 ? true, tree, fetchFromGitHub, hexRegistrySnapshot }: let version = "3.0.0-beta.4"; + bootstrapper = ./rebar3-nix-bootstrap; + # TODO: all these below probably should go into nixpkgs.erlangModules.sources.* # {erlware_commons, "0.16.0"}, erlware_commons = fetchHex { @@ -83,16 +85,18 @@ stdenv.mkDerivation { sha256 = "0px66scjdia9aaa5z36qzxb848r56m0k98g0bxw065a2narsh4xy"; }; + inherit bootstrapper; + patches = if hermeticRebar3 == true then [ ./hermetic-bootstrap.patch ./hermetic-rebar3.patch ] else []; buildInputs = [ erlang tree ]; - propagatedBuildInputs = [ hexRegistrySnapshot rebar3-nix-bootstrap ]; + propagatedBuildInputs = [ hexRegistrySnapshot ]; postPatch = '' echo postPatch - rebar3-nix-bootstrap registry-only + ${erlang}/bin/escript ${bootstrapper} registry-only echo "$ERL_LIBS" mkdir -p _build/default/lib/ mkdir -p _build/default/plugins diff --git a/pkgs/development/tools/build-managers/rebar3/rebar3-nix-bootstrap b/pkgs/development/tools/build-managers/rebar3/rebar3-nix-bootstrap new file mode 100755 index 000000000000..d75d69f054d6 --- /dev/null +++ b/pkgs/development/tools/build-managers/rebar3/rebar3-nix-bootstrap @@ -0,0 +1,256 @@ +#!/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 + , registry_only = false + , compile_ports + , erl_libs + , plugins + , root + , name + , registry_snapshot}). + +-define(HEX_REGISTRY_PATH, ".cache/rebar3/hex/default/registry"). + +main(Args) -> + {ok, ArgData} = parse_args(Args), + {ok, RequiredData} = gather_required_data_from_the_environment(ArgData), + do(RequiredData). + +%% @doc +%% This actually runs the command. There are two modes 'register_only' +%% where the register is created from hex and everything else. +-spec do(#data{}) -> ok. +do(RequiredData = #data{registry_only = true}) -> + ok = bootstrap_registry(RequiredData); +do(RequiredData) -> + ok = bootstrap_registry(RequiredData), + ok = bootstrap_configs(RequiredData), + ok = bootstrap_plugins(RequiredData), + ok = bootstrap_libs(RequiredData). + +%% @doc +%% Argument parsing is super simple only because we want to keep the +%% dependencies minimal. For now there can be two entries on the +%% command line, "register-only" and "compile-ports" +-spec parse_args([string()]) -> #data{}. +parse_args(Args) -> + {ok, #data{registry_only = lists:member("registry-only", Args)}}. + +-spec bootstrap_configs(#data{}) -> ok. +bootstrap_configs(RequiredData)-> + io:format("Boostrapping app and rebar configurations~n"), + ok = if_single_app_project_update_app_src_version(RequiredData), + ok = if_compile_ports_add_pc_plugin(RequiredData). + +-spec bootstrap_plugins(#data{}) -> ok. +bootstrap_plugins(#data{plugins = Plugins}) -> + io:format("Bootstrapping rebar3 plugins~n"), + Target = "_build/default/plugins/", + Paths = string:tokens(Plugins, " "), + CopiableFiles = + lists:foldl(fun(Path, Acc) -> + gather_dependency(Path) ++ Acc + end, [], Paths), + lists:foreach(fun (Path) -> + link_app(Path, Target) + end, CopiableFiles). + +-spec bootstrap_libs(#data{}) -> ok. +bootstrap_libs(#data{erl_libs = ErlLibs}) -> + io:format("Bootstrapping dependent librariesXXXX~n"), + Target = "_build/default/lib/", + Paths = string:tokens(ErlLibs, ":"), + CopiableFiles = + lists:foldl(fun(Path, Acc) -> + gather_directory_contents(Path) ++ Acc + end, [], Paths), + lists:foreach(fun (Path) -> + link_app(Path, Target) + end, CopiableFiles). + +-spec gather_dependency(string()) -> [{string(), string()}]. +gather_dependency(Path) -> + FullLibrary = filename:join(Path, "lib/erlang/lib/"), + case filelib:is_dir(FullLibrary) of + true -> + gather_directory_contents(FullLibrary); + false -> + [raw_hex(Path)] + end. + +-spec raw_hex(string()) -> {string(), string()}. +raw_hex(Path) -> + [_, Name] = re:split(Path, "-hex-source-"), + {Path, erlang:binary_to_list(Name)}. + +-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), + 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 +%% This takes an app name in the standard OTP - format +%% and returns just the app name. Why? because rebar is doesn't +%% respect OTP conventions in some cases. +-spec fixup_app_name(string()) -> string(). +fixup_app_name(FileName) -> + case string:tokens(FileName, "-") of + [Name] -> Name; + [Name, _Version] -> Name + end. + +-spec bootstrap_registry(#data{}) -> ok. +bootstrap_registry(#data{registry_snapshot = RegistrySnapshot}) -> + io:format("Bootstrapping Hex Registry for Rebar~n"), + make_sure_registry_snapshot_exists(RegistrySnapshot), + filelib:ensure_dir(?HEX_REGISTRY_PATH), + ok = case filelib:is_file(?HEX_REGISTRY_PATH) of + true -> + file:delete(?HEX_REGISTRY_PATH); + false -> + ok + end, + ok = file:make_symlink(RegistrySnapshot, + ?HEX_REGISTRY_PATH). + +-spec make_sure_registry_snapshot_exists(string()) -> ok. +make_sure_registry_snapshot_exists(RegistrySnapshot) -> + case filelib:is_file(RegistrySnapshot) of + true -> + ok; + false -> + stderr("Registry snapshot (~s) does not exist!", [RegistrySnapshot]), + erlang:halt(1) + end. + +-spec gather_required_data_from_the_environment(#data{}) -> {ok, map()}. +gather_required_data_from_the_environment(ArgData) -> + {ok, ArgData#data{ version = guard_env("version") + , erl_libs = os:getenv("ERL_LIBS", []) + , plugins = os:getenv("buildPlugins", []) + , root = code:root_dir() + , name = guard_env("name") + , compile_ports = nix2bool(os:getenv("compilePorts", "")) + , registry_snapshot = guard_env("HEX_REGISTRY_SNAPSHOT")}}. + +-spec nix2bool(any()) -> boolean(). +nix2bool("1") -> + true; +nix2bool("") -> + false. + +-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. + +%% @doc +%% If the compile ports flag is set, rewrite the rebar config to +%% include the 'pc' plugin. +-spec if_compile_ports_add_pc_plugin(#data{}) -> ok. +if_compile_ports_add_pc_plugin(#data{compile_ports = true}) -> + ConfigTerms = update_config(read_rebar_config()), + Text = lists:map(fun(Term) -> io_lib:format("~tp.~n", [Term]) end, + ConfigTerms), + file:write_file("rebar.config", Text); +if_compile_ports_add_pc_plugin(_) -> + ok. + +-spec update_config([term()]) -> [term()]. +update_config(Config) -> + case lists:keysearch(plugins, 1, Config) of + {ok, {plugins, PluginList}} -> + lists:keystore(plugins, 1, Config, {plugins, [Config | PluginList]}); + _ -> + [{plugins, [pc]} | Config] + end. + +-spec read_rebar_config() -> [term()]. +read_rebar_config() -> + case file:consult("rebar.config") of + {ok, Terms} -> + Terms; + _ -> + stderr("Unable to read rebar config!", []), + erlang:halt(1) + end. + + +-spec if_single_app_project_update_app_src_version(#data{}) -> ok. +if_single_app_project_update_app_src_version(#data{name = Name, + version = Version}) -> + case app_src_exists(Name) of + {true, SrcFile} -> + update_app_src_with_version(SrcFile, Version); + {false, _} -> + ok + end. + +-spec update_app_src_with_version(string(), string()) -> ok. +update_app_src_with_version(SrcFile, Version) -> + {ok, [{application, Name, Details}]} = file:consult(SrcFile), + NewDetails = lists:keyreplace(vsn, 1, Details, {vsn, Version}), + file:write_file(SrcFile, io_lib:fwrite("~p.\n", [{application, Name, NewDetails}])). + +-spec app_src_exists(string()) -> boolean(). +app_src_exists(Name) -> + FileName = filename:join("src", + lists:concat([Name, ".app.src"])), + {filelib:is_file(FileName), FileName}. + + +%% @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)). diff --git a/pkgs/development/tools/erlang/rebar3-nix-bootstrap/default.nix b/pkgs/development/tools/erlang/rebar3-nix-bootstrap/default.nix deleted file mode 100644 index ed38d573abf1..000000000000 --- a/pkgs/development/tools/erlang/rebar3-nix-bootstrap/default.nix +++ /dev/null @@ -1,24 +0,0 @@ -{stdenv, fetchFromGitHub, erlang }: - -stdenv.mkDerivation rec { - name = "rebar3-nix-bootstrap"; - version = "0.0.3"; - - src = fetchFromGitHub { - owner = "erlang-nix"; - repo = "rebar3-nix-bootstrap"; - rev = "${version}"; - sha256 = "01yyaz104jj3mxx8k10q3rwpn2rh13q1ja5r0iq37qyjmg8xflhq"; - }; - - buildInputs = [ erlang ]; - - installFlags = "PREFIX=$(out)"; - - meta = { - description = "Shim command to help bootstrap a rebar3 project on Nix"; - license = stdenv.lib.licenses.asl20; - homepage = "https://github.com/erlang-nix/rebar3-nix-bootstrap"; - maintainers = with stdenv.lib.maintainers; [ ericbmerritt ]; - }; -} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 02a1721ab178..3b0f9db8f167 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -5311,7 +5311,6 @@ in rebar = callPackage ../development/tools/build-managers/rebar { }; rebar3-open = callPackage ../development/tools/build-managers/rebar3 { hermeticRebar3 = false; }; rebar3 = callPackage ../development/tools/build-managers/rebar3 { hermeticRebar3 = true; }; - rebar3-nix-bootstrap = callPackage ../development/tools/erlang/rebar3-nix-bootstrap { }; hexRegistrySnapshot = callPackage ../development/beam-modules/hex-registry-snapshot.nix { }; fetchHex = callPackage ../development/beam-modules/fetch-hex.nix { };