query
On this page

makeBinWriter

pkgs.writers.makeBinWriter

Functor
Docs pulled from | This Revision | 10 minutes ago


makeBinWriter returns a derivation which compiles the given script into an executable format.

This function is the base implementation for other compile language writers, such as writeHaskell and writeRust.

Inputs

config (AttrSet)
compileScript (String)
The script that compiles the given content into an executable.
strip (Boolean, Default: true)
Whether to strip the executable or not.
makeWrapperArgs (Optional, [ String ], Default: [])
Arguments forwarded to (makeWrapper)[#fun-makeWrapper]
nameOrPath (String)
The name of the script or the path to the script. When a string starting with "/" is passed, the script will be created at the specified path in $out. For example, "/bin/hello" will create a script at $out/bin/hello. Any other string is interpreted as a filename. It must be a POSIX filename starting with a letter, digit, dot, or underscore. Spaces or special characters are not allowed.

Examples

pkgs.writers.makeBinWriter example

// main.c
#include <stdio.h>

int main()
{
    printf("Hello, World!\n");
    return 0;
}
:b makeBinWriter { compileScript = "${pkgs.gcc}/bin/gcc -o $out $contentPath"; } "hello" ./main.c
out -> /nix/store/f6crc8mwj3lvcxqclw7n09cm8nb6kxbh-hello

The above example creates an executable named hello that outputs Hello, World! when executed.

> /nix/store/f6crc8mwj3lvcxqclw7n09cm8nb6kxbh-hello
Hello, World!

Noogle detected

This is a Functor

Learn about functors

Implementation

The following is the current implementation of this function.

makeBinWriter =
    {
      compileScript,
      strip ? true,
      makeWrapperArgs ? [ ],
    }:
    nameOrPath: content:
    assert
      (types.path.check nameOrPath)
      || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
    assert (types.path.check content) || (types.str.check content);
    let
      nameIsPath = types.path.check nameOrPath;
      name = last (builtins.split "/" nameOrPath);
      path = if nameIsPath then nameOrPath else "/bin/${name}";
      # The inner derivation which creates the executable under $out/bin (never at $out directly)
      # This is required in order to support wrapping, as wrapped programs consist of at least two files: the executable and the wrapper.
      inner =
        pkgs.runCommandLocal name
          (
            {
              inherit makeWrapperArgs;
              nativeBuildInputs = [ makeBinaryWrapper ];
              meta.mainProgram = name;
            }
            // (
              if (types.str.check content) then
                {
                  inherit content;
                  passAsFile = [ "content" ];
                }
              else
                { contentPath = content; }
            )
          )
          ''
            ${compileScript}
            ${lib.optionalString strip "${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"}
            # Sometimes binaries produced for darwin (e. g. by GHC) won't be valid
            # mach-o executables from the get-go, but need to be corrected somehow
            # which is done by fixupPhase.
            ${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"}
            mv $out tmp
            mkdir -p $out/$(dirname "${path}")
            mv tmp $out/${path}
            if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then
              wrapProgram $out/${path} ''${makeWrapperArgs[@]}
            fi
          '';
    in
    if nameIsPath then
      inner
    # In case nameOrPath is a name, the user intends the executable to be located at $out.
    # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}.
    # This breaks the override pattern.
    # In case this turns out to be a problem, we can still add more magic
    else
      pkgs.runCommandLocal name { } ''
        ln -s ${inner}/bin/${name} $out
      '';