···8484| fake | attrset | [directives](#controlling-resolution-with-directives) |
8585| fix | attrset | [directives](#controlling-resolution-with-directives) |
8686| keep | attrset | [directives](#controlling-resolution-with-directives) |
8787+| lore | string | [lore directory](#controlling-nested-resolution-with-lore) |
8888+| execers | list | [execer lore directive](#controlling-nested-resolution-with-lore) |
8989+| wrappers | list | [wrapper lore directive](#controlling-nested-resolution-with-lore) |
87908891## Controlling resolution with directives
8992···156159 "~/.bashrc" = true;
157160};
158161```
162162+163163+## Controlling nested resolution with lore
164164+165165+Initially, resolution of commands in the arguments to command-executing
166166+commands was limited to one level for a hard-coded list of builtins and
167167+external commands. resholve can now resolve these recursively.
168168+169169+This feature combines information (_lore_) that the resholve Nix API
170170+obtains via binlore ([nixpkgs](../../tools/analysis/binlore), [repo](https://github.com/abathur/resholve)),
171171+with some rules (internal to resholve) for locating sub-executions in
172172+some of the more common commands.
173173+174174+- "execer" lore identifies whether an executable can, cannot,
175175+ or might execute its arguments. Every "can" or "might" verdict requires
176176+ either built-in rules for finding the executable, or human triage.
177177+- "wrapper" lore maps shell exec wrappers to the programs they exec so
178178+ that resholve can substitute an executable's verdict for its wrapper's.
179179+180180+There will be more mechanisms for controlling this process in the future
181181+(and your reports/experiences will play a role in shaping them...) For now,
182182+the main lever is the ability to substitute your own lore. This is how you'd
183183+do it piecemeal:
184184+185185+```
186186+# --execer 'cannot:${openssl.bin}/bin/openssl can:${openssl.bin}/bin/c_rehash'
187187+execer = [
188188+ /*
189189+ This is the same verdict binlore will
190190+ come up with. It's a no-op just to demo
191191+ how to fiddle lore via the Nix API.
192192+ */
193193+ "cannot:${openssl.bin}/bin/openssl"
194194+ # different verdict, but not used
195195+ "can:${openssl.bin}/bin/c_rehash"
196196+];
197197+198198+# --wrapper '${gnugrep}/bin/egrep:${gnugrep}/bin/grep'
199199+execer = [
200200+ /*
201201+ This is the same verdict binlore will
202202+ come up with. It's a no-op just to demo
203203+ how to fiddle lore via the Nix API.
204204+ */
205205+ "${gnugrep}/bin/egrep:${gnugrep}/bin/grep"
206206+];
207207+```
208208+209209+The format is fairly simple to generate--you can script your own generator if
210210+you need to modify the lore.
···11-{ stdenv, lib, resholve }:
11+{ stdenv, lib, resholve, binlore }:
2233{ pname
44, src
···1010let
1111 inherit stdenv;
1212 /* These functions break up the work of partially validating the
1313- * 'solutions' attrset and massaging it into env/cli args.
1414- *
1515- * Note: some of the left-most args do not *have* to be passed as
1616- * deep as they are, but I've done so to provide more error context
1717- */
1313+ 'solutions' attrset and massaging it into env/cli args.
1414+1515+ Note: some of the left-most args do not *have* to be passed as
1616+ deep as they are, but I've done so to provide more error context
1717+ */
18181919 # for brevity / line length
2020 spaces = l: builtins.concatStringsSep " " l;
···7272 /* Build a single resholve invocation */
7373 makeInvocation = solution: value:
7474 if validateSolution value then
7575- "${makeEnvs solution value} resholve --overwrite ${makeArgs value}"
7575+ # we pass resholve a directory
7676+ "RESHOLVE_LORE=${binlore.collect { drvs = value.inputs; } } ${makeEnvs solution value} resholve --overwrite ${makeArgs value}"
7677 else throw "invalid solution"; # shouldn't trigger for now
77787879 /* Build resholve invocation for each solution. */
···8788 # enable below for verbose debug info if needed
8889 # supports default python.logging levels
8990 # LOGLEVEL="INFO";
9191+ /*
9292+ subshell/PS4/set -x and : command to output resholve envs
9393+ and invocation. Extra context makes it clearer what the
9494+ Nix API is doing, makes nix-shell debugging easier, etc.
9595+ */
9096 preFixup = ''
9191- pushd "$out"
9292- ${builtins.concatStringsSep "\n" (makeCommands solutions)}
9393- popd
9797+ (
9898+ cd "$out"
9999+ PS4=$'\x1f'"\033[33m[resholve context]\033[0m "
100100+ set -x
101101+ : changing directory to $PWD
102102+ ${builtins.concatStringsSep "\n" (makeCommands solutions)}
103103+ )
94104 '';
95105 }));
96106in
+9-44
pkgs/development/misc/resholve/resholve.nix
···22, callPackage
33, python27Packages
44, installShellFiles
55-, fetchFromGitHub
66-, file
77-, findutils
88-, gettext
99-, bats
1010-, bash
1111-, doCheck ? true
55+, rSrc
66+, version
77+, oildev
88+, binlore
129}:
1313-let
1414- version = "0.5.1";
1515- rSrc = fetchFromGitHub {
1616- owner = "abathur";
1717- repo = "resholve";
1818- rev = "v${version}";
1919- hash = "sha256-+9MjvO1H+A3Ol2to5tWqdpNR7osQsYcbkX9avAqyrKw=";
2020- };
2121- deps = callPackage ./deps.nix {
2222- /*
2323- resholve needs to patch Oil, but trying to avoid adding
2424- them all *to* nixpkgs, since they aren't specific to
2525- nix/nixpkgs.
2626- */
2727- oilPatches = [
2828- "${rSrc}/0001-add_setup_py.patch"
2929- "${rSrc}/0002-add_MANIFEST_in.patch"
3030- "${rSrc}/0003-fix_codegen_shebang.patch"
3131- "${rSrc}/0004-disable-internal-py-yajl-for-nix-built.patch"
3232- "${rSrc}/0005_revert_libc_locale.patch"
3333- "${rSrc}/0006_disable_failing_libc_tests.patch"
3434- "${rSrc}/0007_restore_root_init_py.patch"
3535- ];
3636- };
3737-in
1010+3811python27Packages.buildPythonApplication {
3912 pname = "resholve";
4013 inherit version;
4114 src = rSrc;
4215 format = "other";
1616+ dontBuild = true;
43174418 nativeBuildInputs = [ installShellFiles ];
45194646- propagatedBuildInputs = [ deps.oildev python27Packages.configargparse ];
2020+ propagatedBuildInputs = [ oildev python27Packages.configargparse ];
47214822 patchPhase = ''
4923 for file in resholve; do
···5630 installManPage resholve.1
5731 '';
58325959- inherit doCheck;
6060- checkInputs = [ bats ];
6161- RESHOLVE_PATH = "${lib.makeBinPath [ file findutils gettext ]}";
6262-6363- checkPhase = ''
6464- # explicit interpreter for test suite
6565- export INTERP="${bash}/bin/bash" PATH="$out/bin:$PATH"
6666- patchShebangs .
6767- ./test.sh
6868- '';
6969-7033 # Do not propagate Python; may be obsoleted by nixos/nixpkgs#102613
7134 # for context on why, see abathur/resholve#20
7235 postFixup = ''
7336 rm $out/nix-support/propagated-build-inputs
7437 '';
3838+3939+ passthru.tests = callPackage ./test.nix { inherit rSrc; inherit binlore; };
75407641 meta = with lib; {
7742 description = "Resolve external shell-script dependencies";
···11+{ lib
22+, fetchFromGitHub
33+, runCommand
44+, yallback
55+, yara
66+}:
77+88+/* TODO/CAUTION:
99+1010+I don't want to discourage use, but I'm not sure how stable
1111+the API is. Have fun, but be prepared to track changes! :)
1212+1313+For _now_, binlore is basically a thin wrapper around
1414+`<invoke yara> | <postprocess with yallback>` with support
1515+for running it on a derivation, saving the result in the
1616+store, and aggregating results from a set of packages.
1717+1818+In the longer term, I suspect there are more uses for this
1919+general pattern (i.e., run some analysis tool that produces
2020+a deterministic output and cache the result per package...).
2121+2222+I'm not sure how that'll look and if it'll be the case that
2323+binlore automatically collects all of them, or if you'll be
2424+configuring which "kind(s)" of lore it generates. Nailing
2525+that down will almost certainly mean reworking the API.
2626+2727+*/
2828+2929+let
3030+ src = fetchFromGitHub {
3131+ owner = "abathur";
3232+ repo = "binlore";
3333+ rev = "v0.1.3";
3434+ hash = "sha256-+rgv8gAQ3ptOpL/EhbKr/jq+k/4Lpn06/2qON+Ps2wE=";
3535+ };
3636+ /*
3737+ binlore has one one more yallbacks responsible for
3838+ routing the appropriate lore to a named file in the
3939+ appropriate format. At some point I might try to do
4040+ something fancy with this, but for now the answer to
4141+ *all* questions about the lore are: the bare minimum
4242+ to get resholve over the next feature hump in time to
4343+ hopefully slip this feature in before the branch-off.
4444+ */
4545+ # TODO: feeling really uninspired on the API
4646+ loreDef = {
4747+ # YARA rule file
4848+ rules = (src + /execers.yar);
4949+ # output filenames; "types" of lore
5050+ types = [ "execers" "wrappers" ];
5151+ # shell rule callbacks; see github.com/abathur/yallback
5252+ yallback = (src + /execers.yall);
5353+ # TODO:
5454+ # - echo for debug, can be removed at some point
5555+ # - I really just wanted to put the bit after the pipe
5656+ # in here, but I'm erring on the side of flexibility
5757+ # since this form will make it easier to pilot other
5858+ # uses of binlore.
5959+ callback = lore: drv: overrides: ''
6060+ if [[ -d "${drv}/bin" ]]; then
6161+ echo generating binlore for $drv by running:
6262+ echo "${yara}/bin/yara ${lore.rules} ${drv}/bin | ${yallback}/bin/yallback ${lore.yallback}"
6363+ else
6464+ echo "failed to generate binlore for $drv (${drv}/bin doesn't exist)"
6565+ fi
6666+ '' +
6767+ /*
6868+ Override lore for some packages. Unsure, but for now:
6969+ 1. start with the ~name (pname-version)
7070+ 2. remove characters from the end until we find a match
7171+ in overrides/
7272+ 3. execute the override script with the list of expected
7373+ lore types
7474+ */
7575+ ''
7676+ i=''${#identifier}
7777+ filter=
7878+ while [[ $i > 0 ]] && [[ -z "$filter" ]]; do
7979+ if [[ -f "${overrides}/''${identifier:0:$i}" ]]; then
8080+ filter="${overrides}/''${identifier:0:$i}"
8181+ echo using "${overrides}/''${identifier:0:$i}" to generate overriden binlore for $drv
8282+ break
8383+ fi
8484+ ((i--)) || true # don't break build
8585+ done # || true # don't break build
8686+ if [[ -d "${drv}/bin" ]]; then
8787+ ${yara}/bin/yara ${lore.rules} ${drv}/bin | ${yallback}/bin/yallback ${lore.yallback} "$filter"
8888+ fi
8989+ '';
9090+ };
9191+ overrides = (src + /overrides);
9292+9393+in rec {
9494+ collect = { lore ? loreDef, drvs }: (runCommand "more-binlore" { } ''
9595+ mkdir $out
9696+ for lorefile in ${toString lore.types}; do
9797+ cat ${lib.concatMapStrings (x: x + "/$lorefile ") (map (make lore) (map lib.getBin drvs))} > $out/$lorefile
9898+ done
9999+ '');
100100+ # TODO: echo for debug, can be removed at some point
101101+ make = lore: drv: runCommand "${drv.name}-binlore" {
102102+ identifier = drv.name;
103103+ drv = drv;
104104+ } (''
105105+ mkdir $out
106106+ touch $out/{${builtins.concatStringsSep "," lore.types}}
107107+108108+ ${lore.callback lore drv overrides}
109109+110110+ echo binlore for $drv written to $out
111111+ '');
112112+}