How do I set up a flake to be used as a build input

Hi, I’m a bit of a noob, trying to get a workflow up an running using flakes.

I’ve got two repo’s, each with a flake.nix in it. They’re called i3f-schema and gnize. The goal is for gnize to depend on i3f-schema.

gnize is a nim application, so I’m using GitHub - nix-community/flake-nimble: Nimble packages Nix flake [maintainer=@ehmry] to build it. Here’s its flake.nix:

{
  description = "gnize";

  inputs = {
    nixpkgs.url = github:NixOS/nixpkgs/22.05;
    flake-utils.url = github:numtide/flake-utils;
    flake-nimble.url = github:nix-community/flake-nimble;
    i3f-schema = {
      url = "/home/matt/src/i3f-schema";
    };
  };

  outputs = { self, nixpkgs, flake-utils, flake-nimble, i3f-schema }:

    flake-utils.lib.eachDefaultSystem (system:
      let
        version = "0.0.1";
        pkgs = nixpkgs.legacyPackages.${system};
        nimblePkgs = flake-nimble.packages.${system};
      in {
        packages.gnize = pkgs.nimPackages.buildNimPackage {
          name = "gnize";
          inherit version;
          #buildInputs= [ 
          #  i3f-schema
          #];
          src = ./.;
        };

        defaultPackage = self.packages.${system}.gnize;
}

This works, I can nix build && ./result/bin/gnize and everything seems ok. But if I comment out this part:

          #buildInputs= [ 
          #  i3f-schema
          #];

Then I get the following error:

error: Dependency is not of a valid type: element 1 of buildInputs for gnize

the dependency I need out of i3f-schema is a json schema file. Its flake looks like this:

{
  description = "i3f-schema";

  inputs = {
    nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable;
    flake-utils.url = github:numtide/flake-utils;
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let pkgs = nixpkgs.legacyPackages.${system}; in
      rec {

        schema = ./i3f.schema.json;

        packages.i3f-schema = pkgs.stdenv.mkDerivation {
          name = "i3f-schema";
          builder = "${pkgs.bash}/bin/bash";
          args = [
            "-c"
            "${pkgs.coreutils}/bin/cp ${schema} $out"
          ];
          inherit system;
        };

        defaultPackage = self.packages.${system}.i3f-schema;
      }
    );
}

So the question is: what changes do I need to make (to either flake) so that I can reference an env var in the gnize build which points me at the schema file in the nix store?

I’m not attached to buildInputs but it would seem that that’s related to how flake-nimble is going to want non-nim dependencies.

The i3f-schema variable contains the outputs of the i3f-schema flake, not a package. If you want a specific output, you’ll need to access it like i3f-schema.packages.${system}.i3f-schema.

1 Like

Thanks for the reply @tejing. That makes sense, I’m getting the whole flake as an input, it’s up to me to explicitly say which of its outputs I want. Making this change:

          buildInputs= [
            i3f-schema.packages.${system}.i3f-schema.out
          ];

…changes the error to:

❯ nix build
error: builder for '/nix/store/i0a7awa0if3zlbrgpqz9vgli31d2z815-gnize.drv' failed with exit code 127;
       last 1 log lines:
       > /nix/store/d9lydnahvhsmyh7xllqnrfbq37r59zyf-i3f-schema: line 1: placeholder: command not found

Which makes some sense because placeholder is what I have in that file for now. Why are the contents of that file being run as a commnand?

I have noticed that if I change the i3f-schema flake so that the output is a directory and not a file…

          builder = "${pkgs.bash}/bin/bash";
          args = [
            "-c"
            ''
            ${pkgs.coreutils}/bin/mkdir $out
            ${pkgs.coreutils}/bin/cp ${schema} $out
            ''
          ];

… then (after a nix flake update) I can build gnize with no errors. Presumably the file that I want to reference is now in the nix store, so I just need to find a way to reference it. I think the build code is here: nixpkgs/default.nix at 122a7435fb67bedfd54d700b04ccb826298c6e05 · NixOS/nixpkgs · GitHub

I can see that it references nativeBuildInputs but I don’t know how to get a reference to the correct filename into my nim code.

Sorry if this takes us out-of-scope for this forum. I’m trying to do something like this: CastilloDel's page except instead of fizzbuzz, I want to load the json file at compile time.

I know that its filename will be like /nix/store/somethingsomething, but how can I tell my nim code what that somethingsomething is? Perhaps with an environment variable?

The derivations that you put in buildInputs or nativeBuildInputs normally have a directory structure that’s roughly FHS-like, with bin, lib, share, etc as appropriate. The point of those attributes is to list the derivations that stdenv should automatically set up the build environment to use, adding bin to PATH, making sure compilers look there for header files, etc.

If you want to use a derivation that just produces a single file, you generally do so by using a string antiquote to inject the store path of the derivation directly into whatever build command will be using it instead, since stdenv can’t really automatically figure out what it’s supposed to do with the file.

Also, just a note here: There’s absolutely no need to be as roundabout as to create a derivation whose only purpose is to copy a file to $out. You can simply use a path type, and it will be automatically copied to the nix store when you antiquote it into a string. The entire i3f-schema flake seems incredibly overengineered, really. It could just be a single file in a directory with no flake.nix, and the input could be marked with flake = false;, then you can just use ${i3f-schema}/i3f.schema.json in your gnize derivation.

1 Like

Oh, this is making sense now.

I had to follow the thread through nixpkgs a bit, but I wound up here: nixpkgs/default.nix at 75a5ebbfba6adc61627b1667d12ebda21375a2d1 · NixOS/nixpkgs · GitHub

There I could see that a default buildPhase was used only if I didn’t supply my own. So I supplied my own:

        packages.gnize = pkgs.nimPackages.buildNimPackage {
          name = "gnize";
          inherit version;
          buildPhase = ''
            runHook preBuild
            export I3F_SCHEMA=${i3f-schema}/i3f.schema.json
            nim_builder --phase:build
            runHook postBuild
            '';
          src = ./.;
        };

Following this, the env var I3F_SCHEMA had a path into the nix store at compile time, which I was then able to read in gnize like so:

import os

proc readSchema(): string {.compileTime.} =
  let key = "I3F_SCHEMA"
  if existsEnv(key):
    let path = getEnv(key)
    return readFile(path)
  else:
    raise newException(Exception, key & "is unset")

const i3fSchema = readSchema()

Thanks very much for your help