Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crystal package builder #67510

Merged
merged 6 commits into from Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 71 additions & 0 deletions doc/languages-frameworks/crystal.section.md
@@ -0,0 +1,71 @@
# Crystal

## Building a Crystal package

This section uses [Mint](https://github.com/mint-lang/mint) as an example for how to build a Crystal package.

If the Crystal project has any dependencies, the first step is to get a `shards.nix` file encoding those. Get a copy of the project and go to its root directory such that its `shard.lock` file is in the current directory, then run `crystal2nix` in it
```bash
$ git clone https://github.com/mint-lang/mint
$ cd mint
$ git checkout 0.5.0
$ nix-shell -p crystal2nix --run crystal2nix
```

This should have generated a `shards.nix` file.

Next create a Nix file for your derivation and use `pkgs.crystal.buildCrystalPackage` as follows:
```nix
with import <nixpkgs> {};
crystal.buildCrystalPackage rec {
pname = "mint";
version = "0.5.0";

src = fetchFromGitHub {
owner = "mint-lang";
repo = "mint";
rev = version;
sha256 = "0vxbx38c390rd2ysvbwgh89v2232sh5rbsp3nk9wzb70jybpslvl";
};

# Insert the path to your shards.nix file here
shardsFile = ./shards.nix;

...
}
```

This won't build anything yet, because we haven't told it what files build. We can specify a mapping from binary names to source files with the `crystalBinaries` attribute. The project's compilation instructions should show this. For Mint, the binary is called "mint", which is compiled from the source file `src/mint.cr`, so we'll specify this as follows:

```nix
crystalBinaries.mint.src = "src/mint.cr";

# ...
```

Additionally you can override the default `crystal build` options (which are currently `--release --progress --no-debug --verbose`) with

```nix
crystalBinaries.mint.options = [ "--release" "--verbose" ];
```

Depending on the project, you might need additional steps to get it to compile successfully. In Mint's case, we need to link against openssl, so in the end the Nix file looks as follows:

```nix
with import <nixpkgs> {};
crystal.buildCrystalPackage rec {
version = "0.5.0";
pname = "mint";
src = fetchFromGitHub {
owner = "mint-lang";
repo = "mint";
rev = version;
sha256 = "0vxbx38c390rd2ysvbwgh89v2232sh5rbsp3nk9wzb70jybpslvl";
};

shardsFile = ./shards.nix;
crystalBinaries.mint.src = "src/mint.cr";

buildInputs = [ openssl_1_0_2 ];
}
```
1 change: 1 addition & 0 deletions doc/languages-frameworks/index.xml
Expand Up @@ -32,4 +32,5 @@
<xi:include href="titanium.section.xml" />
<xi:include href="vim.section.xml" />
<xi:include href="emscripten.section.xml" />
<xi:include href="crystal.section.xml" />
</chapter>
53 changes: 53 additions & 0 deletions pkgs/development/compilers/crystal/build-package.nix
@@ -0,0 +1,53 @@
{ stdenv, lib, crystal, linkFarm, fetchFromGitHub }:
{ # Generate shards.nix with `nix-shell -p crystal2nix --run crystal2nix` in the projects root
shardsFile ? null
# Specify binaries to build in the form { foo.src = "src/foo.cr"; }
# The default `crystal build` options can be overridden with { foo.options = [ "--no-debug" ]; }
, crystalBinaries ? {}
, ...
}@args:
let
mkDerivationArgs = builtins.removeAttrs args [ "shardsFile" "crystalBinaries" ];

crystalLib = linkFarm "crystal-lib" (lib.mapAttrsToList (name: value: {
inherit name;
path = fetchFromGitHub value;
}) (import shardsFile));

defaultOptions = [ "--release" "--progress" "--no-debug" "--verbose" ];

in stdenv.mkDerivation (mkDerivationArgs // {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be nicer to allow overriding of all attributes by inverting the merge order?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@manveru For all attributes I'm setting in the derivation, I'm also propagating the ones from args (if any). This order is needed because otherwise e.g. the users buildInputs would override our changed buildInputs which contains crystal.


configurePhase = args.configurePhase or ''
runHook preConfigure
${lib.optionalString (shardsFile != null) "ln -s ${crystalLib} lib"}
runHook postConfigure
'';

buildInputs = args.buildInputs or [] ++ [ crystal ];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the [] ++ needed here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah same reason, in case args doesn't have buildInputs set

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was reading the precedence wrong... really would prefer parenthesis in this case :)


buildPhase = args.buildPhase or ''
runHook preBuild
${lib.concatStringsSep "\n" (lib.mapAttrsToList (bin: attrs: ''
crystal ${lib.escapeShellArgs ([
"build"
"-o" bin
(attrs.src or (throw "No source file for crystal binary ${bin} provided"))
] ++ attrs.options or defaultOptions)}
'') crystalBinaries)}
runHook postBuild
'';

installPhase = args.installPhase or ''
runHook preInstall
mkdir -p "$out/bin"
${lib.concatMapStringsSep "\n" (bin: ''
mv ${lib.escapeShellArgs [ bin "${placeholder "out"}/bin/${bin}" ]}
'') (lib.attrNames crystalBinaries)}
runHook postInstall
'';

meta = args.meta or {} // {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why the {} // is needed here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's parsed as (args.meta or {}) // { ... }, so this prevents it from failing when args doesn't have meta

platforms = args.meta.platforms or crystal.meta.platforms;
};
})
Expand Up @@ -26,7 +26,7 @@ File.open "shards.nix", "w+" do |file|

sha256 = ""
args = ["--url", url, "--rev", rev]
Process.run("nix-prefetch-git", args: args) do |x|
Process.run("@nixPrefetchGit@", args: args) do |x|
x.error.each_line { |e| puts e }
sha256 = PrefetchJSON.from_json(x.output).sha256
end
Expand Down
16 changes: 16 additions & 0 deletions pkgs/development/compilers/crystal/crystal2nix.nix
@@ -0,0 +1,16 @@
{ lib, crystal, nix-prefetch-git }:
crystal.buildCrystalPackage {
pname = "crystal2nix";
version = "unstable-2018-07-31";

nixPrefetchGit = "${lib.getBin nix-prefetch-git}/bin/nix-prefetch-git";
unpackPhase = "substituteAll ${./crystal2nix.cr} crystal2nix.cr";

crystalBinaries.crystal2nix.src = "crystal2nix.cr";

meta = with lib; {
description = "Utility to convert Crystal's shard.lock files to a Nix file";
license = licenses.mit;
maintainers = [ maintainers.manveru ];
};
}
13 changes: 10 additions & 3 deletions pkgs/development/compilers/crystal/default.nix
@@ -1,6 +1,7 @@
{ stdenv, lib, fetchFromGitHub, fetchurl, makeWrapper
, coreutils, git, gmp, nettools, openssl, readline, tzdata, libxml2, libyaml
, boehmgc, libatomic_ops, pcre, libevent, libiconv, llvm, clang, which, zlib }:
, boehmgc, libatomic_ops, pcre, libevent, libiconv, llvm, clang, which, zlib
, callPackage }:

# We need multiple binaries as a given binary isn't always able to build
# (even slightly) older or newer versions.
Expand Down Expand Up @@ -37,7 +38,7 @@ let
};

generic = { version, sha256, binary, doCheck ? true }:
stdenv.mkDerivation rec {
let compiler = stdenv.mkDerivation rec {
pname = "crystal";
inherit doCheck version;

Expand Down Expand Up @@ -134,14 +135,18 @@ let
export PATH=${lib.makeBinPath checkInputs}:$PATH
'';

passthru.buildCrystalPackage = callPackage ./build-package.nix {
crystal = compiler;
};

meta = with lib; {
description = "A compiled language with Ruby like syntax and type inference";
homepage = https://crystal-lang.org/;
license = licenses.asl20;
maintainers = with maintainers; [ manveru david50407 peterhoeg ];
platforms = builtins.attrNames archs;
};
};
}; in compiler;

in rec {
binaryCrystal_0_26 = genericBinary {
Expand Down Expand Up @@ -207,4 +212,6 @@ in rec {
};

crystal = crystal_0_30;

crystal2nix = callPackage ./crystal2nix.nix {};
}
59 changes: 11 additions & 48 deletions pkgs/development/compilers/mint/default.nix
@@ -1,64 +1,27 @@
# Updating the dependencies for this package:
#
# wget https://raw.githubusercontent.com/mint-lang/mint/0.3.1/shard.lock
# nix-shell -p crystal libyaml --run 'crystal run crystal2nix.cr'
#
{stdenv, lib, fetchFromGitHub, crystal, zlib, openssl, duktape, which, libyaml }:
let
crystalPackages = lib.mapAttrs (name: src:
stdenv.mkDerivation {
name = lib.replaceStrings ["/"] ["-"] name;
src = fetchFromGitHub src;
phases = "installPhase";
installPhase = ''cp -r $src $out'';
passthru = { libName = name; };
}
) (import ./shards.nix);

crystalLib = stdenv.mkDerivation {
name = "crystal-lib";
src = lib.attrValues crystalPackages;
libNames = lib.mapAttrsToList (k: v: [k v]) crystalPackages;
phases = "buildPhase";
buildPhase = ''
mkdir -p $out
linkup () {
while [ "$#" -gt 0 ]; do
ln -s $2 $out/$1
shift; shift
done
}
linkup $libNames
'';
};
in
stdenv.mkDerivation rec {
{ lib, fetchFromGitHub, crystal, zlib, openssl_1_0_2, duktape, which, libyaml }:
crystal.buildCrystalPackage rec {
version = "0.5.0";
name = "mint-${version}";
pname = "mint";
src = fetchFromGitHub {
owner = "mint-lang";
repo = "mint";
rev = version;
sha256 = "0vxbx38c390rd2ysvbwgh89v2232sh5rbsp3nk9wzb70jybpslvl";
};

nativeBuildInputs = [ which crystal zlib openssl duktape libyaml ];

buildPhase = ''
mkdir -p $out/bin tmp
cd tmp
ln -s ${crystalLib} lib
cp -r $src/* .
crystal build src/mint.cr -o $out/bin/mint --verbose --progress --release --no-debug
'';
buildInputs = [ openssl_1_0_2 ];

installPhase = ''true'';
# Update with
# nix-shell -p crystal2nix --run crystal2nix
# with mint's shard.lock file in the current directory
shardsFile = ./shards.nix;
crystalBinaries.mint.src = "src/mint.cr";

meta = {
description = "A refreshing language for the front-end web";
homepage = https://mint-lang.com/;
license = stdenv.lib.licenses.bsd3;
maintainers = with stdenv.lib.maintainers; [ manveru ];
license = lib.licenses.bsd3;
maintainers = with lib.maintainers; [ manveru ];
platforms = [ "x86_64-linux" "i686-linux" "x86_64-darwin" ];
};
}
3 changes: 2 additions & 1 deletion pkgs/top-level/all-packages.nix
Expand Up @@ -7453,7 +7453,8 @@ in
crystal_0_27
crystal_0_29
crystal_0_30
crystal;
crystal
crystal2nix;

icr = callPackage ../development/tools/icr {};

Expand Down