diff --git a/doc/languages-frameworks/beam.section.md b/doc/languages-frameworks/beam.section.md index b5d39595c10..947968db15e 100644 --- a/doc/languages-frameworks/beam.section.md +++ b/doc/languages-frameworks/beam.section.md @@ -24,6 +24,10 @@ Many Erlang/OTP distributions available in `beam.interpreters` have versions wit We provide a version of Rebar3, under `rebar3`. We also provide a helper to fetch Rebar3 dependencies from a lockfile under `fetchRebar3Deps`. +We also provide a version on Rebar3 with plugins included, under `rebar3WithPlugins`. This package is a function which takes two arguments: `plugins`, a list of nix derivations to include as plugins (loaded only when specified in `rebar.config`), and `globalPlugins`, which should always be loaded by rebar3. Example: `rebar3WithPlugins { globalPlugins = [beamPackages.pc]; }`. + +When adding a new plugin it is important that the `packageName` attribute is the same as the atom used by rebar3 to refer to the plugin. + ### Mix & Erlang.mk {#build-tools-other} Erlang.mk works exactly as expected. There is a bootstrap process that needs to be run, which is supported by the `buildErlangMk` derivation. diff --git a/pkgs/development/beam-modules/default.nix b/pkgs/development/beam-modules/default.nix index 6bf9e45e622..19b5c122d9e 100644 --- a/pkgs/development/beam-modules/default.nix +++ b/pkgs/development/beam-modules/default.nix @@ -18,8 +18,8 @@ let inherit callPackage erlang; beamPackages = self; + inherit (callPackage ../tools/build-managers/rebar3 { }) rebar3 rebar3WithPlugins; rebar = callPackage ../tools/build-managers/rebar { }; - rebar3 = callPackage ../tools/build-managers/rebar3 { }; # rebar3 port compiler plugin is required by buildRebar3 pc = callPackage ./pc { }; diff --git a/pkgs/development/tools/build-managers/rebar3/default.nix b/pkgs/development/tools/build-managers/rebar3/default.nix index b9070a42764..b44a8c08ffa 100644 --- a/pkgs/development/tools/build-managers/rebar3/default.nix +++ b/pkgs/development/tools/build-managers/rebar3/default.nix @@ -1,71 +1,104 @@ { lib, stdenv, fetchFromGitHub, - fetchHex, erlang, - tree }: + fetchHex, erlang, makeWrapper }: let version = "3.15.1"; + owner = "erlang"; + deps = import ./rebar-deps.nix { inherit fetchHex fetchFromGitHub; }; + rebar3 = stdenv.mkDerivation rec { + pname = "rebar3"; + inherit version erlang; - # Dependencies should match the ones in: - # https://github.com/erlang/rebar3/blob/${version}/rebar.lock - # `sha256` could also be taken from https://hex.pm - Checksum - deps = import ./rebar-deps.nix { inherit fetchHex; }; -in -stdenv.mkDerivation rec { - pname = "rebar3"; - inherit version erlang; + # How to obtain `sha256`: + # nix-prefetch-url --unpack https://github.com/erlang/rebar3/archive/${version}.tar.gz + src = fetchFromGitHub { + inherit owner; + repo = pname; + rev = version; + sha256 = "1pcy5m79g0l9l3d8lkbx6cq1w87z1g3sa6wwvgbgraj2v3wkyy5g"; + }; + + bootstrapper = ./rebar3-nix-bootstrap; + + buildInputs = [ erlang ]; + + postPatch = '' + mkdir -p _checkouts _build/default/lib/ + + ${toString (lib.mapAttrsToList (k: v: '' + cp -R --no-preserve=mode ${v} _checkouts/${k} + '') deps)} + + # Bootstrap script expects the dependencies in _build/default/lib + # TODO: Make it accept checkouts? + for i in _checkouts/* ; do + ln -s $(pwd)/$i $(pwd)/_build/default/lib/ + done + ''; + + buildPhase = '' + HOME=. escript bootstrap + ''; + + installPhase = '' + mkdir -p $out/bin + cp rebar3 $out/bin/rebar3 + ''; + + meta = { + homepage = "https://github.com/rebar/rebar3"; + description = "Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases"; + + longDescription = '' + rebar is a self-contained Erlang script, so it's easy to distribute or + even embed directly in a project. Where possible, rebar uses standard + Erlang/OTP conventions for project structures, thus minimizing the amount + of build configuration work. rebar also provides dependency management, + enabling application writers to easily re-use common libraries from a + variety of locations (hex.pm, git, hg, and so on). + ''; + + platforms = lib.platforms.unix; + maintainers = lib.teams.beam.members; + license = lib.licenses.asl20; + }; - # How to obtain `sha256`: - # nix-prefetch-url --unpack https://github.com/erlang/rebar3/archive/${version}.tar.gz - src = fetchFromGitHub { - owner = "erlang"; - repo = pname; - rev = version; - sha256 = "1pcy5m79g0l9l3d8lkbx6cq1w87z1g3sa6wwvgbgraj2v3wkyy5g"; }; + rebar3WithPlugins = { plugins ? [ ], globalPlugins ? [ ] }: + let + pluginLibDirs = map (p: "${p}/lib/erlang/lib") (lib.unique (plugins ++ globalPlugins)); + globalPluginNames = lib.unique (map (p: p.packageName) globalPlugins); + rebar3Patched = (rebar3.overrideAttrs (old: { - bootstrapper = ./rebar3-nix-bootstrap; + # skip-plugins.patch is necessary because otherwise rebar3 will always + # try to fetch plugins if they are not already present in _build. + # + # global-deps.patch makes it possible to use REBAR_GLOBAL_PLUGINS to + # instruct rebar3 to always load a certain plugin. It is necessary since + # REBAR_GLOBAL_CONFIG_DIR doesn't seem to work for this. + patches = [ ./skip-plugins.patch ./global-plugins.patch ]; + })); + in stdenv.mkDerivation { + pname = "rebar3-with-plugins"; + inherit (rebar3) version bootstrapper; + nativeBuildInputs = [ erlang makeWrapper ]; + unpackPhase = "true"; - buildInputs = [ erlang tree ]; + # Here we extract the rebar3 escript (like `rebar3_prv_local_install.erl`) and + # add plugins to the code path. - postPatch = '' - mkdir -p _checkouts - mkdir -p _build/default/lib/ - - ${toString (lib.mapAttrsToList (k: v: '' - cp -R --no-preserve=mode ${v} _checkouts/${k} - '') deps)} - - # Bootstrap script expects the dependencies in _build/default/lib - # TODO: Make it accept checkouts? - for i in _checkouts/* ; do - ln -s $(pwd)/$i $(pwd)/_build/default/lib/ - done - ''; - - buildPhase = '' - HOME=. escript bootstrap - ''; - - installPhase = '' - mkdir -p $out/bin - cp rebar3 $out/bin/rebar3 - ''; - - meta = { - homepage = "https://github.com/rebar/rebar3"; - description = "Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases"; - - longDescription = '' - rebar is a self-contained Erlang script, so it's easy to distribute or - even embed directly in a project. Where possible, rebar uses standard - Erlang/OTP conventions for project structures, thus minimizing the amount - of build configuration work. rebar also provides dependency management, - enabling application writers to easily re-use common libraries from a - variety of locations (hex.pm, git, hg, and so on). + installPhase = '' + erl -noshell -eval ' + {ok, Escript} = escript:extract("${rebar3Patched}/bin/rebar3", []), + {archive, Archive} = lists:keyfind(archive, 1, Escript), + {ok, _} = zip:extract(Archive, [{cwd, "'$out/lib'"}]), + init:stop(0) + ' + mkdir -p $out/bin + makeWrapper ${erlang}/bin/erl $out/bin/rebar3 \ + --set REBAR_GLOBAL_PLUGINS "${toString globalPluginNames}" \ + --suffix-each ERL_LIBS ":" "$out/lib ${toString pluginLibDirs}" \ + --add-flags "+sbtu +A1 -noshell -boot start_clean -s rebar3 main -extra" ''; - - platforms = lib.platforms.unix; - maintainers = lib.teams.beam.members; - license = lib.licenses.asl20; - }; -} + }; +in { inherit rebar3 rebar3WithPlugins; } diff --git a/pkgs/development/tools/build-managers/rebar3/global-plugins.patch b/pkgs/development/tools/build-managers/rebar3/global-plugins.patch new file mode 100644 index 00000000000..9a8bb48f988 --- /dev/null +++ b/pkgs/development/tools/build-managers/rebar3/global-plugins.patch @@ -0,0 +1,14 @@ +diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl +index f2d22233..bee2cf18 100644 +--- a/src/rebar_plugins.erl ++++ b/src/rebar_plugins.erl +@@ -30,7 +30,8 @@ project_plugins_install(State) -> + top_level_install(State) -> + Profiles = rebar_state:current_profiles(State), + lists:foldl(fun(Profile, StateAcc) -> +- Plugins = rebar_state:get(State, {plugins, Profile}, []), ++ Plugins = rebar_state:get(State, {plugins, Profile}, []) ++ ++ [list_to_atom(P) || P <- string:lexemes(os:getenv("REBAR_GLOBAL_PLUGINS", ""), " ")], + handle_plugins(Profile, Plugins, StateAcc) + end, State, Profiles). + diff --git a/pkgs/development/tools/build-managers/rebar3/skip-plugins.patch b/pkgs/development/tools/build-managers/rebar3/skip-plugins.patch new file mode 100644 index 00000000000..3ee1656c9ed --- /dev/null +++ b/pkgs/development/tools/build-managers/rebar3/skip-plugins.patch @@ -0,0 +1,54 @@ +diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl +index f2d22233..c61fa553 100644 +--- a/src/rebar_plugins.erl ++++ b/src/rebar_plugins.erl +@@ -106,31 +106,9 @@ handle_plugins(Profile, Plugins, State, Upgrade) -> + State3 = rebar_state:set(State2, deps_dir, DepsDir), + rebar_state:lock(State3, Locks). + +-handle_plugin(Profile, Plugin, State, Upgrade) -> ++handle_plugin(_Profile, Plugin, State, _Upgrade) -> + try +- {Apps, State2} = rebar_prv_install_deps:handle_deps_as_profile(Profile, State, [Plugin], Upgrade), +- {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps), +- ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []), +- +- %% Add already built plugin deps to the code path +- ToBuildPaths = [rebar_app_info:ebin_dir(A) || A <- ToBuild], +- PreBuiltPaths = [Ebin || A <- Apps, +- Ebin <- [rebar_app_info:ebin_dir(A)], +- not lists:member(Ebin, ToBuildPaths)], +- code:add_pathsa(PreBuiltPaths), +- +- %% Build plugin and its deps +- build_plugins(ToBuild, Apps, State2), +- +- %% Add newly built deps and plugin to code path +- State3 = rebar_state:update_all_plugin_deps(State2, Apps), +- NewCodePaths = [rebar_app_info:ebin_dir(A) || A <- ToBuild], +- +- %% Store plugin code paths so we can remove them when compiling project apps +- State4 = rebar_state:update_code_paths(State3, all_plugin_deps, PreBuiltPaths++NewCodePaths), +- rebar_paths:set_paths([plugins], State4), +- +- {plugin_providers(Plugin), State4} ++ {plugin_providers(Plugin), State} + catch + ?WITH_STACKTRACE(C,T,S) + ?DEBUG("~p ~p ~p", [C, T, S]), +@@ -138,15 +116,6 @@ handle_plugin(Profile, Plugin, State, Upgrade) -> + {[], State} + end. + +-build_plugins(MustBuildApps, AllApps, State) -> +- State1 = rebar_state:deps_to_build(State, MustBuildApps), +- State2 = rebar_state:all_deps(State1, AllApps), +- State3 = rebar_state:set(State2, deps_dir, ?DEFAULT_PLUGINS_DIR), +- {Args, Extra} = rebar_state:command_parsed_args(State), +- State4 = rebar_state:command_parsed_args(State3, {[{deps_only, true}|Args], Extra}), +- rebar_prv_compile:do(State4), +- ok. +- + plugin_providers({Plugin, _, _, _}) when is_atom(Plugin) -> + validate_plugin(Plugin); + plugin_providers({Plugin, _, _}) when is_atom(Plugin) -> diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 67f7e4fffc8..e7cfe7eaf95 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -11855,7 +11855,7 @@ in erlang_nox = beam_nox.interpreters.erlang; inherit (beam.packages.erlang) - rebar rebar3 + rebar rebar3 rebar3WithPlugins fetchHex beamPackages relxExe;