Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1{ lib, writeTextFile, buildPackages }: 2 3# All possible values as defined by the spec, version 1.4. 4# Please keep in spec order for easier maintenance. 5# When adding a new value, don't forget to update the Version field below! 6# See https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html 7lib.makeOverridable ({ name # The name of the desktop file 8, type ? "Application" 9# version is hardcoded 10, desktopName # The name of the application 11, genericName ? null 12, noDisplay ? null 13, comment ? null 14, icon ? null 15# we don't support the Hidden key - if you don't need something, just don't install it 16, onlyShowIn ? [] 17, notShowIn ? [] 18, dbusActivatable ? null 19, tryExec ? null 20, exec ? null 21, path ? null 22, terminal ? null 23, actions ? {} # An attrset of [internal name] -> { name, exec?, icon? } 24, mimeTypes ? [] # The spec uses "MimeType" as singular, use plural here to signify list-ness 25, categories ? [] 26, implements ? [] 27, keywords ? [] 28, startupNotify ? null 29, startupWMClass ? null 30, url ? null 31, prefersNonDefaultGPU ? null 32# not supported until version 1.5, which is not supported by our desktop-file-utils as of 2022-02-23 33# , singleMainWindow ? null 34, extraConfig ? {} # Additional values to be added literally to the final item, e.g. vendor extensions 35}: 36let 37 # There are multiple places in the FDO spec that make "boolean" values actually tristate, 38 # e.g. StartupNotify, where "unset" is literally defined as "do something reasonable". 39 # So, handle null values separately. 40 boolOrNullToString = value: 41 if value == null then null 42 else if builtins.isBool value then lib.boolToString value 43 else throw "makeDesktopItem: value must be a boolean or null!"; 44 45 # Multiple values are represented as one string, joined by semicolons. 46 # Technically, it's possible to escape semicolons in values with \;, but this is currently not implemented. 47 renderList = key: value: 48 if !builtins.isList value then throw "makeDesktopItem: value for ${key} must be a list!" 49 else if builtins.any (item: lib.hasInfix ";" item) value then throw "makeDesktopItem: values in ${key} list must not contain semicolons!" 50 else if value == [] then null 51 else builtins.concatStringsSep ";" value; 52 53 # The [Desktop Entry] section of the desktop file, as an attribute set. 54 # Please keep in spec order. 55 mainSection = { 56 "Type" = type; 57 "Version" = "1.4"; 58 "Name" = desktopName; 59 "GenericName" = genericName; 60 "NoDisplay" = boolOrNullToString noDisplay; 61 "Comment" = comment; 62 "Icon" = icon; 63 "OnlyShowIn" = renderList "onlyShowIn" onlyShowIn; 64 "NotShowIn" = renderList "notShowIn" notShowIn; 65 "DBusActivatable" = boolOrNullToString dbusActivatable; 66 "TryExec" = tryExec; 67 "Exec" = exec; 68 "Path" = path; 69 "Terminal" = boolOrNullToString terminal; 70 "Actions" = renderList "actions" (builtins.attrNames actions); 71 "MimeType" = renderList "mimeTypes" mimeTypes; 72 "Categories" = renderList "categories" categories; 73 "Implements" = renderList "implements" implements; 74 "Keywords" = renderList "keywords" keywords; 75 "StartupNotify" = boolOrNullToString startupNotify; 76 "StartupWMClass" = startupWMClass; 77 "URL" = url; 78 "PrefersNonDefaultGPU" = boolOrNullToString prefersNonDefaultGPU; 79 # "SingleMainWindow" = boolOrNullToString singleMainWindow; 80 } // extraConfig; 81 82 # Render a single attribute pair to a Key=Value line. 83 # FIXME: this isn't entirely correct for arbitrary strings, as some characters 84 # need to be escaped. There are currently none in nixpkgs though, so this is OK. 85 renderLine = name: value: if value != null then "${name}=${value}" else null; 86 87 # Render a full section of the file from an attrset. 88 # Null values are intentionally left out. 89 renderSection = sectionName: attrs: 90 lib.pipe attrs [ 91 (lib.mapAttrsToList renderLine) 92 (builtins.filter (v: v != null)) 93 (builtins.concatStringsSep "\n") 94 (section: '' 95 [${sectionName}] 96 ${section} 97 '') 98 ]; 99 100 mainSectionRendered = renderSection "Desktop Entry" mainSection; 101 102 # Convert from javaCase names as used in Nix to PascalCase as used in the spec. 103 preprocessAction = { name, icon ? null, exec ? null }: { 104 "Name" = name; 105 "Icon" = icon; 106 "Exec" = exec; 107 }; 108 renderAction = name: attrs: renderSection "Desktop Action ${name}" (preprocessAction attrs); 109 actionsRendered = lib.mapAttrsToList renderAction actions; 110 111 content = [ mainSectionRendered ] ++ actionsRendered; 112in 113writeTextFile { 114 name = "${name}.desktop"; 115 destination = "/share/applications/${name}.desktop"; 116 text = builtins.concatStringsSep "\n" content; 117 checkPhase = ''${buildPackages.desktop-file-utils}/bin/desktop-file-validate "$target"''; 118})