1<section xmlns="http://docbook.org/ns/docbook" 2 xmlns:xlink="http://www.w3.org/1999/xlink" 3 xml:id="sec-beam"> 4 5 <title>BEAM Languages (Erlang, Elixir &amp; LFE)</title> 6 <section xml:id="beam-introduction"> 7 <title>Introduction</title> 8 <para> 9 In this document and related Nix expressions, we use the term, 10 <emphasis>BEAM</emphasis>, to describe the environment. BEAM is the name 11 of the Erlang Virtual Machine and, as far as we're concerned, from a 12 packaging perspective, all languages that run on the BEAM are 13 interchangeable. That which varies, like the build system, is transparent 14 to users of any given BEAM package, so we make no distinction. 15 </para> 16 </section> 17 <section xml:id="beam-structure"> 18 <title>Structure</title> 19 <para> 20 All BEAM-related expressions are available via the top-level 21 <literal>beam</literal> attribute, which includes: 22 </para> 23 <itemizedlist> 24 <listitem> 25 <para> 26 <literal>interpreters</literal>: a set of compilers running on the 27 BEAM, including multiple Erlang/OTP versions 28 (<literal>beam.interpreters.erlangR19</literal>, etc), Elixir 29 (<literal>beam.interpreters.elixir</literal>) and LFE 30 (<literal>beam.interpreters.lfe</literal>). 31 </para> 32 </listitem> 33 <listitem> 34 <para> 35 <literal>packages</literal>: a set of package sets, each compiled with 36 a specific Erlang/OTP version, e.g. 37 <literal>beam.packages.erlangR19</literal>. 38 </para> 39 </listitem> 40 </itemizedlist> 41 <para> 42 The default Erlang compiler, defined by 43 <literal>beam.interpreters.erlang</literal>, is aliased as 44 <literal>erlang</literal>. The default BEAM package set is defined by 45 <literal>beam.packages.erlang</literal> and aliased at the top level as 46 <literal>beamPackages</literal>. 47 </para> 48 <para> 49 To create a package set built with a custom Erlang version, use the 50 lambda, <literal>beam.packagesWith</literal>, which accepts an Erlang/OTP 51 derivation and produces a package set similar to 52 <literal>beam.packages.erlang</literal>. 53 </para> 54 <para> 55 Many Erlang/OTP distributions available in 56 <literal>beam.interpreters</literal> have versions with ODBC and/or Java 57 enabled. For example, there's 58 <literal>beam.interpreters.erlangR19_odbc_javac</literal>, which 59 corresponds to <literal>beam.interpreters.erlangR19</literal>. 60 </para> 61 <para xml:id="erlang-call-package"> 62 We also provide the lambda, 63 <literal>beam.packages.erlang.callPackage</literal>, which simplifies 64 writing BEAM package definitions by injecting all packages from 65 <literal>beam.packages.erlang</literal> into the top-level context. 66 </para> 67 </section> 68 <section xml:id="build-tools"> 69 <title>Build Tools</title> 70 <section xml:id="build-tools-rebar3"> 71 <title>Rebar3</title> 72 <para> 73 By default, Rebar3 wants to manage its own dependencies. This is perfectly 74 acceptable in the normal, non-Nix setup, but in the Nix world, it is not. 75 To rectify this, we provide two versions of Rebar3: 76 <itemizedlist> 77 <listitem> 78 <para> 79 <literal>rebar3</literal>: patched to remove the ability to download 80 anything. When not running it via <literal>nix-shell</literal> or 81 <literal>nix-build</literal>, it's probably not going to work as 82 desired. 83 </para> 84 </listitem> 85 <listitem> 86 <para> 87 <literal>rebar3-open</literal>: the normal, unmodified Rebar3. It 88 should work exactly as would any other version of Rebar3. Any Erlang 89 package should rely on <literal>rebar3</literal> instead. See <xref 90 linkend="rebar3-packages"/>. 91 </para> 92 </listitem> 93 </itemizedlist> 94 </para> 95 </section> 96 <section xml:id="build-tools-other"> 97 <title>Mix &amp; Erlang.mk</title> 98 <para> 99 Both Mix and Erlang.mk work exactly as expected. There is a bootstrap 100 process that needs to be run for both, however, which is supported by the 101 <literal>buildMix</literal> and <literal>buildErlangMk</literal> 102 derivations, respectively. 103 </para> 104 </section> 105</section> 106 107<section xml:id="how-to-install-beam-packages"> 108 <title>How to Install BEAM Packages</title> 109 <para> 110 BEAM packages are not registered at the top level, simply because they are 111 not relevant to the vast majority of Nix users. They are installable using 112 the <literal>beam.packages.erlang</literal> attribute set (aliased as 113 <literal>beamPackages</literal>), which points to packages built by the 114 default Erlang/OTP version in Nixpkgs, as defined by 115 <literal>beam.interpreters.erlang</literal>. 116 117 To list the available packages in 118 <literal>beamPackages</literal>, use the following command: 119 </para> 120 121 <programlisting> 122$ nix-env -f &quot;&lt;nixpkgs&gt;&quot; -qaP -A beamPackages 123beamPackages.esqlite esqlite-0.2.1 124beamPackages.goldrush goldrush-0.1.7 125beamPackages.ibrowse ibrowse-4.2.2 126beamPackages.jiffy jiffy-0.14.5 127beamPackages.lager lager-3.0.2 128beamPackages.meck meck-0.8.3 129beamPackages.rebar3-pc pc-1.1.0 130 </programlisting> 131 <para> 132 To install any of those packages into your profile, refer to them by their 133 attribute path (first column): 134 </para> 135 <programlisting> 136$ nix-env -f &quot;&lt;nixpkgs&gt;&quot; -iA beamPackages.ibrowse 137 </programlisting> 138 <para> 139 The attribute path of any BEAM package corresponds to the name of that 140 particular package in <link xlink:href="https://hex.pm">Hex</link> or its 141 OTP Application/Release name. 142 </para> 143</section> 144<section xml:id="packaging-beam-applications"> 145 <title>Packaging BEAM Applications</title> 146 <section xml:id="packaging-erlang-applications"> 147 <title>Erlang Applications</title> 148 <section xml:id="rebar3-packages"> 149 <title>Rebar3 Packages</title> 150 <para> 151 The Nix function, <literal>buildRebar3</literal>, defined in 152 <literal>beam.packages.erlang.buildRebar3</literal> and aliased at the 153 top level, can be used to build a derivation that understands how to 154 build a Rebar3 project. For example, we can build <link 155 xlink:href="https://github.com/erlang-nix/hex2nix">hex2nix</link> as 156 follows: 157 </para> 158 <programlisting> 159 { stdenv, fetchFromGitHub, buildRebar3, ibrowse, jsx, erlware_commons }: 160 161 buildRebar3 rec { 162 name = "hex2nix"; 163 version = "0.0.1"; 164 165 src = fetchFromGitHub { 166 owner = "ericbmerritt"; 167 repo = "hex2nix"; 168 rev = "${version}"; 169 sha256 = "1w7xjidz1l5yjmhlplfx7kphmnpvqm67w99hd2m7kdixwdxq0zqg"; 170 }; 171 172 beamDeps = [ ibrowse jsx erlware_commons ]; 173 } 174 </programlisting> 175 <para> 176 Such derivations are callable with 177 <literal>beam.packages.erlang.callPackage</literal> (see <xref 178 linkend="erlang-call-package"/>). To call this package using the normal 179 <literal>callPackage</literal>, refer to dependency packages via 180 <literal>beamPackages</literal>, e.g. 181 <literal>beamPackages.ibrowse</literal>. 182 </para> 183 <para> 184 Notably, <literal>buildRebar3</literal> includes 185 <literal>beamDeps</literal>, while 186 <literal>stdenv.mkDerivation</literal> does not. BEAM dependencies added 187 there will be correctly handled by the system. 188 </para> 189 <para> 190 If a package needs to compile native code via Rebar3's port compilation 191 mechanism, add <literal>compilePort = true;</literal> to the derivation. 192 </para> 193 </section> 194 <section xml:id="erlang-mk-packages"> 195 <title>Erlang.mk Packages</title> 196 <para> 197 Erlang.mk functions similarly to Rebar3, except we use 198 <literal>buildErlangMk</literal> instead of 199 <literal>buildRebar3</literal>. 200 </para> 201 <programlisting> 202 { buildErlangMk, fetchHex, cowlib, ranch }: 203 204 buildErlangMk { 205 name = "cowboy"; 206 version = "1.0.4"; 207 208 src = fetchHex { 209 pkg = "cowboy"; 210 version = "1.0.4"; 211 sha256 = "6a0edee96885fae3a8dd0ac1f333538a42e807db638a9453064ccfdaa6b9fdac"; 212 }; 213 214 beamDeps = [ cowlib ranch ]; 215 216 meta = { 217 description = '' 218 Small, fast, modular HTTP server written in Erlang 219 ''; 220 license = stdenv.lib.licenses.isc; 221 homepage = https://github.com/ninenines/cowboy; 222 }; 223 } 224 </programlisting> 225 </section> 226 <section xml:id="mix-packages"> 227 <title>Mix Packages</title> 228 <para> 229 Mix functions similarly to Rebar3, except we use 230 <literal>buildMix</literal> instead of <literal>buildRebar3</literal>. 231 </para> 232 <programlisting> 233 { buildMix, fetchHex, plug, absinthe }: 234 235 buildMix { 236 name = "absinthe_plug"; 237 version = "1.0.0"; 238 239 src = fetchHex { 240 pkg = "absinthe_plug"; 241 version = "1.0.0"; 242 sha256 = "08459823fe1fd4f0325a8bf0c937a4520583a5a26d73b193040ab30a1dfc0b33"; 243 }; 244 245 beamDeps = [ plug absinthe ]; 246 247 meta = { 248 description = '' 249 A plug for Absinthe, an experimental GraphQL toolkit 250 ''; 251 license = stdenv.lib.licenses.bsd3; 252 homepage = https://github.com/CargoSense/absinthe_plug; 253 }; 254 } 255 </programlisting> 256 <para> 257 Alternatively, we can use <literal>buildHex</literal> as a shortcut: 258 </para> 259 <programlisting> 260 { buildHex, buildMix, plug, absinthe }: 261 262 buildHex { 263 name = "absinthe_plug"; 264 version = "1.0.0"; 265 266 sha256 = "08459823fe1fd4f0325a8bf0c937a4520583a5a26d73b193040ab30a1dfc0b33"; 267 268 builder = buildMix; 269 270 beamDeps = [ plug absinthe ]; 271 272 meta = { 273 description = '' 274 A plug for Absinthe, an experimental GraphQL toolkit 275 ''; 276 license = stdenv.lib.licenses.bsd3; 277 homepage = https://github.com/CargoSense/absinthe_plug; 278 }; 279 } 280 </programlisting> 281 </section> 282 </section> 283</section> 284<section xml:id="how-to-develop"> 285 <title>How to Develop</title> 286 <section xml:id="accessing-an-environment"> 287 <title>Accessing an Environment</title> 288 <para> 289 Often, we simply want to access a valid environment that contains a 290 specific package and its dependencies. We can accomplish that with the 291 <literal>env</literal> attribute of a derivation. For example, let's say 292 we want to access an Erlang REPL with <literal>ibrowse</literal> loaded 293 up. We could do the following: 294 </para> 295 <programlisting> 296 $ nix-shell -A beamPackages.ibrowse.env --run "erl" 297 Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] 298 299 Eshell V7.0 (abort with ^G) 300 1> m(ibrowse). 301 Module: ibrowse 302 MD5: 3b3e0137d0cbb28070146978a3392945 303 Compiled: January 10 2016, 23:34 304 Object file: /nix/store/g1rlf65rdgjs4abbyj4grp37ry7ywivj-ibrowse-4.2.2/lib/erlang/lib/ibrowse-4.2.2/ebin/ibrowse.beam 305 Compiler options: [{outdir,"/tmp/nix-build-ibrowse-4.2.2.drv-0/hex-source-ibrowse-4.2.2/_build/default/lib/ibrowse/ebin"}, 306 debug_info,debug_info,nowarn_shadow_vars, 307 warn_unused_import,warn_unused_vars,warnings_as_errors, 308 {i,"/tmp/nix-build-ibrowse-4.2.2.drv-0/hex-source-ibrowse-4.2.2/_build/default/lib/ibrowse/include"}] 309 Exports: 310 add_config/1 send_req_direct/7 311 all_trace_off/0 set_dest/3 312 code_change/3 set_max_attempts/3 313 get_config_value/1 set_max_pipeline_size/3 314 get_config_value/2 set_max_sessions/3 315 get_metrics/0 show_dest_status/0 316 get_metrics/2 show_dest_status/1 317 handle_call/3 show_dest_status/2 318 handle_cast/2 spawn_link_worker_process/1 319 handle_info/2 spawn_link_worker_process/2 320 init/1 spawn_worker_process/1 321 module_info/0 spawn_worker_process/2 322 module_info/1 start/0 323 rescan_config/0 start_link/0 324 rescan_config/1 stop/0 325 send_req/3 stop_worker_process/1 326 send_req/4 stream_close/1 327 send_req/5 stream_next/1 328 send_req/6 terminate/2 329 send_req_direct/4 trace_off/0 330 send_req_direct/5 trace_off/2 331 send_req_direct/6 trace_on/0 332 trace_on/2 333 ok 334 2> 335 </programlisting> 336 <para> 337 Notice the <literal>-A beamPackages.ibrowse.env</literal>. That is the key 338 to this functionality. 339 </para> 340 </section> 341 <section xml:id="creating-a-shell"> 342 <title>Creating a Shell</title> 343 <para> 344 Getting access to an environment often isn't enough to do real 345 development. Usually, we need to create a <literal>shell.nix</literal> 346 file and do our development inside of the environment specified therein. 347 This file looks a lot like the packaging described above, except that 348 <literal>src</literal> points to the project root and we call the package 349 directly. 350 </para> 351 <programlisting> 352{ pkgs ? import &quot;&lt;nixpkgs&quot;&gt; {} }: 353 354with pkgs; 355 356let 357 358 f = { buildRebar3, ibrowse, jsx, erlware_commons }: 359 buildRebar3 { 360 name = "hex2nix"; 361 version = "0.1.0"; 362 src = ./.; 363 beamDeps = [ ibrowse jsx erlware_commons ]; 364 }; 365 drv = beamPackages.callPackage f {}; 366 367in 368 369 drv 370 </programlisting> 371 <section xml:id="building-in-a-shell"> 372 <title>Building in a Shell (for Mix Projects)</title> 373 <para> 374 We can leverage the support of the derivation, irrespective of the build 375 derivation, by calling the commands themselves. 376 </para> 377 <programlisting> 378# ============================================================================= 379# Variables 380# ============================================================================= 381 382NIX_TEMPLATES := "$(CURDIR)/nix-templates" 383 384TARGET := "$(PREFIX)" 385 386PROJECT_NAME := thorndyke 387 388NIXPKGS=../nixpkgs 389NIX_PATH=nixpkgs=$(NIXPKGS) 390NIX_SHELL=nix-shell -I "$(NIX_PATH)" --pure 391# ============================================================================= 392# Rules 393# ============================================================================= 394.PHONY= all test clean repl shell build test analyze configure install \ 395 test-nix-install publish plt analyze 396 397all: build 398 399guard-%: 400 @ if [ "${${*}}" == "" ]; then \ 401 echo "Environment variable $* not set"; \ 402 exit 1; \ 403 fi 404 405clean: 406 rm -rf _build 407 rm -rf .cache 408 409repl: 410 $(NIX_SHELL) --run "iex -pa './_build/prod/lib/*/ebin'" 411 412shell: 413 $(NIX_SHELL) 414 415configure: 416 $(NIX_SHELL) --command 'eval "$$configurePhase"' 417 418build: configure 419 $(NIX_SHELL) --command 'eval "$$buildPhase"' 420 421install: 422 $(NIX_SHELL) --command 'eval "$$installPhase"' 423 424test: 425 $(NIX_SHELL) --command 'mix test --no-start --no-deps-check' 426 427plt: 428 $(NIX_SHELL) --run "mix dialyzer.plt --no-deps-check" 429 430analyze: build plt 431 $(NIX_SHELL) --run "mix dialyzer --no-compile" 432 433 </programlisting> 434 <para> 435 Using a <literal>shell.nix</literal> as described (see <xref 436 linkend="creating-a-shell"/>) should just work. Aside from 437 <literal>test</literal>, <literal>plt</literal>, and 438 <literal>analyze</literal>, the Make targets work just fine for all of the 439 build derivations. 440 </para> 441 </section> 442</section> 443</section> 444<section xml:id="generating-packages-from-hex-with-hex2nix"> 445 <title>Generating Packages from Hex with <literal>hex2nix</literal></title> 446 <para> 447 Updating the <link xlink:href="https://hex.pm">Hex</link> package set 448 requires <link 449 xlink:href="https://github.com/erlang-nix/hex2nix">hex2nix</link>. Given the 450 path to the Erlang modules (usually 451 <literal>pkgs/development/erlang-modules</literal>), it will dump a file 452 called <literal>hex-packages.nix</literal>, containing all the packages that 453 use a recognized build system in <link 454 xlink:href="https://hex.pm">Hex</link>. It can't be determined, however, 455 whether every package is buildable. 456 </para> 457 <para> 458 To make life easier for our users, try to build every <link 459 xlink:href="https://hex.pm">Hex</link> package and remove those that fail. 460 To do that, simply run the following command in the root of your 461 <literal>nixpkgs</literal> repository: 462 </para> 463 <programlisting> 464$ nix-build -A beamPackages 465 </programlisting> 466 <para> 467 That will attempt to build every package in 468 <literal>beamPackages</literal>. Then manually remove those that fail. 469 Hopefully, someone will improve <link 470 xlink:href="https://github.com/erlang-nix/hex2nix">hex2nix</link> in the 471 future to automate the process. 472 </para> 473</section> 474</section>