Skip to content

Commit

Permalink
Add AppImage outputs
Browse files Browse the repository at this point in the history
  • Loading branch information
tek committed Oct 8, 2023
1 parent 6edf950 commit fe4db9a
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 68 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Add the option `haskellTools` globally and per-env, which allows specifying shell tools that should be made available
from the env's GHC without overrides.
* Separate the effect of the option `profiling` from `env.<name>.localPackage`.
* Add flake apps that build AppImage distributions using [nix-appimage](https://github.com/ralismark/nix-appimage) as
`.#appimage`, `.#<exename>.appimage`, `.#env.<envname>.<exename>.appimage`.

# 0.6.7

Expand Down
8 changes: 5 additions & 3 deletions lib/console.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,19 @@

color = n: t: "\\e[" + toString n + "m" + t + "\\e[0m";

bold = color "1";

in {
inherit indentLine color colors colorsBg colorsBright colorsBgBright;
inherit indentLine color colors colorsBg colorsBright colorsBgBright bold;

indent = map indentLine;

bold = color "1";

s.colors = builtins.mapAttrs (_: color) colors;
s.colorsBg = builtins.mapAttrs (_: color) colorsBg;

rgb = r: g: b: color "48;2;${r};${g};${b}";

colorExt = n: color "48;5;${n}";

chevrons = bold (color colors.magenta ">>>");
}
3 changes: 3 additions & 0 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ let
then dep.name
else dep;

app = program: { type = "app"; program = "${program}"; };

in {
inherit
flake-utils
Expand Down Expand Up @@ -154,6 +156,7 @@ in {
evalModules
overridesVia
cabalDepPackage
app
;

ghcOverlay = import ./ghc-overlay.nix;
Expand Down
19 changes: 19 additions & 0 deletions lib/doc/prose.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,25 @@ in {
}
```
### AppImage bundles
Hix can create more portable distributable bundles by using [nix-appimage](https://github.com/ralismark/nix-appimage)
to generate AppImage executables, exposed in the following flake apps:
```
nix run '.#appimage' # The default executable from the default package
nix run '.#<pkgname>.appimage' # The default executable from the specified package
nix run '.#<exename>.appimage' # The specified executable from whatever package defines it
nix run '.#env.<envname>.[<pkg/exe>.]appimage' # The same as above, but from the specified env
```
This will print a store path:
```
>>> AppImage bundle for <exename> created at:
/nix/store/bxbcp9mk9rf0sjg88hxsjqzpql5is280-<exename>-0.1.0.0-x86_64.AppImage
```
'';

misc = ''
Expand Down
7 changes: 4 additions & 3 deletions lib/hackage.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
with builtins;
with lib;
let
inherit (util) app;

pkgs = config.internal.pkgs;

git = "${pkgs.git}/bin/git";

cfg = config.hackage;
gitAdd = cfg.commit || cfg.add;

app = program: { type = "app"; inherit program; };
gitAdd = cfg.commit || cfg.add;

allTargets = if cfg.packages == null then config.internal.packageNames else cfg.packages;

Expand Down Expand Up @@ -94,7 +95,7 @@ let
handleVersion = file:
if file != null then checkVersion file else noVersion;

formatTag = name: config.hackage.formatTag { inherit name; version = "$version"; };
formatTag = name: cfg.formatTag { inherit name; version = "$version"; };

tagFragment = ''
if [[ -n ''${version:-} ]]
Expand Down
105 changes: 96 additions & 9 deletions lib/output.nix
Original file line number Diff line number Diff line change
@@ -1,30 +1,73 @@
{ lib, config, util, ... }:
with lib;
let
inherit (util) app;

envCommand = import ../lib/command.nix { inherit config util; };

depVersions = env: import ../lib/dep-versions.nix { inherit config lib util env; };

releaseDrv = import ../lib/release-derivation.nix {
inherit lib;
inherit (util) hsLib;
};

console = import ./console.nix { inherit lib; };

staticDrv = util.hsLib.justStaticExecutables;

withStatic = pkg: pkg // { static = staticDrv pkg; };
onlyStatic = pkg: { static = staticDrv pkg; };

withStatic = pkg: pkg // onlyStatic pkg;

# TODO this needs to be refactored into the core
allExes = pkg: lib.optionalAttrs pkg.executable.enable { ${pkg.name} = pkg.executable; } //
lib.filterAttrs (_: e: e.enable) pkg.executables;

pkgMainExe = pkg: let
all = allExes pkg;
names = lib.attrNames all;
in
if lib.hasAttr pkg.name all
then all.${pkg.name}.name
else if lib.length names > 0
then all.${lib.head names}
else pkg.executable.name;

mainExe = pkgMainExe config.packages.${config.main};

foldExes = f: let
pkgExes = pkg: lib.genAttrs (lib.attrNames (allExes pkg)) (f pkg);
in util.foldMapAttrs pkgExes (lib.attrValues config.packages);

staticPackageExe = env: pkg: exe: staticDrv (env.ghc.ghc.${pkg.name}.overrideAttrs (_: { pname = exe; }));

staticExes = env:
{
executables.static =
foldExes (staticPackageExe env) //
{ ${config.main} = staticPackageExe env config.packages.${config.main} mainExe.name; };
};

cross = ghc: name: let
mkCross = cname: cpkgs: withStatic cpkgs.hixPackages.${name};
in mapAttrs mkCross ghc.pkgs.pkgsCross;
in lib.mapAttrs mkCross ghc.pkgs.pkgsCross;

withCross = env: name: let
ghc = env.ghc;
in withStatic ghc.ghc.${name} // { cross = cross ghc name; };

envDerivations = v: let
env = config.envs.${v};
in mapAttrs (n: d: (withCross env n)) env.derivations;
in lib.mapAttrs (n: d: (withCross env n)) env.derivations;

fullEnvDerivations = v: let
env = config.envs.${v};
in envDerivations v // {
static = staticDrv env.derivations.${config.main};
} // staticExes env;

prefixedEnvDerivations = envs: let
envPkgs = v: mapAttrs' (n: d: { name = "${v}-${n}"; value = d; }) (envDerivations v);
envPkgs = v: lib.mapAttrs' (n: d: { name = "${v}-${n}"; value = d; }) (envDerivations v);
in util.foldMapAttrs envPkgs envs;

devOutputs = let
Expand All @@ -35,17 +78,61 @@ let
release = releaseDrv ghc.${name};
min = minGhc.${name};
};
local = mapAttrs extra config.envs.dev.derivations;
local = lib.mapAttrs extra config.envs.dev.derivations;
in local // {
default = local.${config.main};
min = local.${config.main}.min;
static = staticDrv local.${config.main};
};

scopedEnvDerivations = envs: genAttrs envs envDerivations;
scopedEnvDerivations = envs: lib.genAttrs envs envDerivations;

envsApi = envs: { env = lib.mapAttrs (n: e: { inherit (e.ghc) pkgs ghc; } // fullEnvDerivations n) envs; };

appimage = env: name: config.pkgs.writeScript "hix-appimage-${name}" ''
set -eu
outlink="hix-appimage-tmp-${name}"
${config.pkgs.nix}/bin/nix \
--extra-experimental-features 'nix-command flakes' \
bundle -o $outlink --bundler github:ralismark/nix-appimage '${config.base}#env.${env}.executables.static.${name}'
store_path=$(${config.pkgs.coreutils}/bin/readlink $outlink)
rm $outlink
echo -e "${console.chevrons} AppImage bundle for ${console.bold (console.color console.colors.blue name)} created at:" >&2
echo $store_path
'';

appimageApp = env: name: { appimage = app (appimage env name); };

envAppimages = env:
appimageApp env.name mainExe.name //
{ ${config.main} = appimageApp env.name config.main; } //
foldExes (pkg: n: appimageApp env.name n);

mainAppimageApp = appimageApp "dev" mainExe.name;

envCommands = env:
lib.mapAttrs (_: command: app "${(envCommand { inherit env command; }).path}") config.commands;

envApps = env: {
${env.name} = envCommands env // envAppimages env // {
dep-versions = app "${depVersions env.name}";
};
};

envsApi = envs: { env = mapAttrs (n: e: { inherit (e.ghc) pkgs ghc; } // envDerivations n) envs; };
commandApps = lib.mapAttrs (_: c: app "${c.path}");

in {
inherit prefixedEnvDerivations scopedEnvDerivations devOutputs envsApi;
inherit
prefixedEnvDerivations
scopedEnvDerivations
devOutputs
envsApi
app
appimageApp
mainAppimageApp
envApps
commandApps
mainExe
pkgMainExe
;
}
3 changes: 1 addition & 2 deletions lib/show-overrides.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ let
spec = import ./deps/spec.nix { inherit lib; };
deps = import ./deps/default.nix { inherit pkgs; };
console = import ./console.nix { inherit lib; };
inherit (console) color bold indent;
inherit (console) color bold indent chevrons;
inherit (console.colors) magenta blue yellow green;

chevronY = bold (color yellow ">");
chevronM = bold (color magenta ">");
chevrons = bold (color magenta ">>>");

renderSource = concatStringsSep (bold (color green " -> "));

Expand Down
23 changes: 15 additions & 8 deletions modules/hpack.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,29 @@ with lib;
with types;
let

libOutput = import ../lib/output.nix { inherit config lib util; };

maybeDefaultApp = name: a:
if name == config.defaultApp
then { default = a; }
else {};

optionalField = name: conf: optionalAttrs (hasAttr name conf) { ${name} = conf.${name}; };

app = pkg: name: exe:
let a = { type = "app"; program = "${pkg}/bin/${name}"; };
appWithAppimage = pkg: name:
util.app "${pkg}/bin/${name}" // libOutput.appimageApp "dev" name;

appField = pkg: name: exe:
let a = appWithAppimage pkg name;
in { ${name} = a; } // maybeDefaultApp name a;

packageApps = outputs: pname: conf:
util.foldAttrs (mapAttrsToList (app (outputs.${pname})) (conf.executables or {}));
packageApps = outputs: pname: conf: let
main = libOutput.pkgMainExe config.packages.${pname};
pkg = outputs.${pname};
in
{ ${pname} = appWithAppimage pkg main.name; } //
util.foldAttrs (mapAttrsToList (appField pkg) (conf.executables or {}))
;

mkPrelude = prelude: base: let
mod = prelude.module;
Expand Down Expand Up @@ -113,8 +123,6 @@ let
benches
];

infer = mapAttrs generatePackageConf config.packages;

script = import ../lib/hpack.nix { inherit config; verbose = true; };

scriptQuiet = import ../lib/hpack.nix { inherit config; };
Expand All @@ -125,8 +133,7 @@ in {
defaultApp = mkOption {
type = str;
description = mdDoc ''
The name of an executable in {option}`packages` that should be assigned to
`packages.default`.
The name of an executable in [](#opt-general-packages) that should be assigned to `packages.default`.
'';
};

Expand Down
32 changes: 10 additions & 22 deletions modules/output.nix
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{ lib, config, util, ... }:
with lib;
with types;
let

envCommand = import ../lib/command.nix { inherit config util; };
inherit (lib) optionalAttrs mkOption;
inherit (util) app;

tags = import ../lib/tags.nix { inherit config; };

Expand All @@ -24,23 +22,13 @@ let
params = ["path"];
};

app = program: { type = "app"; program = "${program}"; };

envApps = env: {
${env.name} = mapAttrs (_: command: app "${(envCommand { inherit env command; }).path}") config.commands // {
dep-versions = app "${depVersions env.name}";
};
};

commandApps = mapAttrs (_: c: app "${c.path}");

genAll = config.pkgs.writeScript "hix-gen-all" ''
${config.hpack.script}
${if config.gen-overrides.enable then genOverrides else ""}
'';

in {
options = {
options = with lib.types; {

output = {

Expand Down Expand Up @@ -121,7 +109,7 @@ in {
config = {

output = {
final = mkDefault config.outputs;
final = lib.mkDefault config.outputs;
};

outputs = {
Expand All @@ -141,15 +129,15 @@ in {

devShells = let

shells = mapAttrs (_: e: e.shell) (filterAttrs (_: util.envSystemAllowed) util.visibleEnvs);
shells = lib.mapAttrs (_: e: e.shell) (lib.filterAttrs (_: util.envSystemAllowed) util.visibleEnvs);

in shells // { default = shells.dev; };

apps = let

exposed = filterAttrs (_: c: c.expose) config.commands;
exposed = lib.filterAttrs (_: c: c.expose) config.commands;

in config.hackage.output.apps // config.hpack.apps // commandApps exposed // {
in config.hackage.output.apps // config.hpack.apps // libOutput.commandApps exposed // {
gen-cabal = app "${config.hpack.script}";
gen-cabal-quiet = app "${config.hpack.scriptQuiet}";
hpack = app "${config.hpack.script}";
Expand All @@ -161,10 +149,10 @@ in {
gen = app "${genAll}";
show-overrides = app "${showOverrides}";
dep-versions = app "${depVersions "dev"}";
} // optionalAttrs config.output.commandApps {
cmd = commandApps config.commands;
} // libOutput.mainAppimageApp // optionalAttrs config.output.commandApps {
cmd = libOutput.commandApps config.commands;
} // optionalAttrs config.output.envApps {
env = util.foldMapAttrs envApps (attrValues util.visibleEnvs);
env = util.foldMapAttrs libOutput.envApps (lib.attrValues util.visibleEnvs);
};

};
Expand Down
Loading

0 comments on commit fe4db9a

Please sign in to comment.