Placeholder to avoid cyclic dependencies?

I was experiencing disturbing things with placeholders like placeholder "out". While I was thinking that it was evaluated at compile time, it seems that in fact it’s replaced with something like an actual placeholder @out@, and that it’s replaced only at build time (and it seems to be confirmed by the documentation in nix manual).

Now, I’m curious, can placeholders be used to refer to the out folder of other derivations, with something like placeholder "myderivation.out" ? It could be quite useful to avoid cyclic dependencies.

And more generally, is there any documentation on placeholders? (nix manual is close to behing empty) It’s not clear to me what I can put in values in expressions like @mylib@?

My usecase would be to make that script working, even if I know that symlinkJoin is a better solution to that specific problem. My primary goal here is mostly to understand placeholders and see what’s possible to do with them or not rather than solving a precise problem:

{ stdenv, fetchFromGitHub, pkgconfig, libusbmuxd, libplist, speex, libav, alsaLib, gtk3, libappindicator-gtk3, libjpeg_turbo, writeShellScriptBin }:
let
  myWrapper = binaryfile: writeShellScriptBin "droidcam-with-modules" ''
    if ! modinfo v4l2loopback_dc; then
      sudo modprobe v4l2loopback-dc
    fi
    if ! modinfo videodev; then
      sudo modprobe videodev
    fi
    if ! modinfo snd_aloop; then
      sudo modprobe snd_aloop
    fi
    ${binaryfile} &
    pacmd load-module module-alsa-source device=hw:Loopback,1,0
    wait
  '';
in
stdenv.mkDerivation rec {
  pname = "droidcam";
  version = "1.5";

  src = fetchFromGitHub {
    owner = "aramg";
    repo = "droidcam";
    rev = "v1.5";
    sha256 = "tIb7wqzAjSHoT9169NiUO+z6w5DrJVYvkQ3OxDqI1DA=";
  };

  sourceRoot = "source/linux";

  buildInputs = [ pkgconfig ];
  nativeBuildInputs = [ libappindicator-gtk3 speex libav gtk3 libjpeg_turbo libusbmuxd libplist alsaLib ];

  makeFlags = [ "JPEG_DIR=${libjpeg_turbo.out}" "JPEG_LIB=${libjpeg_turbo.out}/lib" ];
  postPatch = ''
    sed -i -e 's:(JPEG_LIB)/libturbojpeg.a:(JPEG_LIB)/libturbojpeg.so:' Makefile
    substituteInPlace src/droidcam.c --replace "/opt/droidcam-icon.png" "$out/share/icons/hicolor/droidcam.png"
  '';

  installPhase = let myplaceholder = (placeholder "out") + "/bin/droidcam"; in ''
    echo ${myplaceholder}
    echo ${placeholder "out"}
    # Copy the executables in the bin folder, creating automatically the subfolders
    install -Dt $out/bin droidcam droidcam-cli ${myWrapper myplaceholder}/bin/droidcam-with-modules
    # Same for pictures
    install -D icon2.png $out/share/icons/hicolor/droidcam.png
  '';

  meta = with stdenv.lib; {
    description = "DroidCam Linux client";
    homepage = https://github.com/aramg/droidcam;
  };
}
1 Like

The point of placeholders isn’t to avoid dependencies. It’s to get the value of usual output references ($out, $dev, etc.) during evaluation time already. Of course, it’s a kind-of hack; IIRC it has a dummy hash value that gets rewritten some time later.

Oh, I don’t want to avoid dependencies, just do cross references between them. For example, does that mean that I have no way to have for example in /nix/store/XXX-a/ a file pointing to /nix/store/YYY-b, and conversely a file in /nix/store/YYY-b pointing in /nix/store/XXX-a?

Cyclic dependencies are not possible in /nix/store/. And real cycles aren’t possible during evaluation time either.

Of course, we do “fake cycles”… e.g. sometimes we cut the cycle by creating a minimal version of a package that doesn’t depend on the problematic thing, so you have that package twice there but set up differently.

Ok I see thank you! But if these cycles are problematic in practice (I guess it increases the closure size, and could lead to strange bug if two flavors of one package are used), why can’t we use placeholders to solve the issue in a more elegant way? Just that nobody took the time to write it, or is there some more fundamental reasons?

To me cycles among /nix/store paths don’t make much sense (except self-references). Any strongly-connected component has to be handled as a unit (manipulation like copying, garbage collection, etc.). There seems not much advantage in splitting them into separate paths when they can’t really be handled separately.

Makes sense. Thank you !

The hash in the output path of a (non-fixed-output) derivation is, among other things, based on the hash of the derivation (.drv). In the case of a cycle, you cannot compute the hash of the derivation, since the contents of the derivation are (through the cycle) dependent on that hash. [1]

If you would stub the output path of A in the derivation of B to break the cycle, then the output path of B is not unique anymore. That is, B built with different flavors of A would result in the same output path for B.

(Stubbing output paths does work in the case of a self-reference and is used to compute the hash of non-fixed-output derivations.)

[1] Nix Pill 18 has a nice description of how output paths are computed: Nix Pills | Nix & NixOS

Thanks for the answer. I understand that cyclic dependencies are impossible due to the hash thing. I was thinking before that it may be possible however to remove the explicit dependency of one of the dependency (like A points to B, but B does not point to A) and then create for example a “meta derivation” C whose goal was to include both derivations A and B. That way, the derivation C completely fixes both A and B and people would install C instead of A and B. But I guess it may be an issue for nix to know what to do if someone asks to simply compile B without A, and the benefit is not really clear anyway. So problem solved, thanks a lot !