withPackages
pkgs.emacsPackages.withPackages
Docs pulled from | This Revision | about 1 hour ago
Contribute
Enhance the ecosystem with your expertise! Contribute to fill the gaps in documentation. Your input can make a difference.
Noogle detected
Implementation
The following is the current implementation of this function.
packagesFun: # packages explicitly requested by the user
let
explicitRequires = if lib.isFunction packagesFun then packagesFun self else packagesFun;
in
runCommand (lib.appendToName "with-packages" emacs).name
{
inherit emacs explicitRequires;
nativeBuildInputs = [
emacs
lndir
makeBinaryWrapper
];
preferLocalBuild = true;
allowSubstitutes = false;
# Store all paths we want to add to emacs here, so that we only need to add
# one path to the load lists
deps =
runCommand "emacs-packages-deps"
(
{
inherit explicitRequires lndir emacs;
}
// lib.optionalAttrs withNativeCompilation {
inherit (emacs) LIBRARY_PATH;
}
)
''
findInputsOld() {
local pkg="$1"; shift
local var="$1"; shift
local propagatedBuildInputsFiles=("$@")
# TODO(@Ericson2314): Restore using associative array once Darwin
# nix-shell doesn't use impure bash. This should replace the O(n)
# case with an O(1) hash map lookup, assuming bash is implemented
# well :D.
local varSlice="$var[*]"
# ''${..-} to hack around old bash empty array problem
case " ''${!varSlice-} " in
*" $pkg "*) return 0 ;;
esac
unset -v varSlice
eval "$var"'+=("$pkg")'
if ! [ -e "$pkg" ]; then
echo "build input $pkg does not exist" >&2
exit 1
fi
local file
for file in "''${propagatedBuildInputsFiles[@]}"; do
file="$pkg/nix-support/$file"
[[ -f "$file" ]] || continue
local pkgNext
for pkgNext in $(< "$file"); do
findInputsOld "$pkgNext" "$var" "''${propagatedBuildInputsFiles[@]}"
done
done
}
mkdir -p $out/bin
mkdir -p $out/share/emacs/site-lisp
${lib.optionalString withNativeCompilation ''
mkdir -p $out/share/emacs/native-lisp
''}
${lib.optionalString withTreeSitter ''
mkdir -p $out/lib
''}
local requires
for pkg in $explicitRequires; do
findInputsOld $pkg requires propagated-user-env-packages
done
# requires now holds all requested packages and their transitive dependencies
linkPath() {
local pkg=$1
local origin_path=$2
local dest_path=$3
# Add the path to the search path list, but only if it exists.
# Executables in /bin are linked by their resolved paths in case they are
# relative symlinks (which break when 'lndir'ed as is);
# see https://github.com/NixOS/nixpkgs/issues/395442
if [[ -d "$pkg/$origin_path" ]]; then
case "$origin_path" in
bin)
for exe in $pkg/$origin_path/*; do
ln -s "$(realpath "$exe")" "$out/$dest_path/$(basename "$exe")"
done
;;
*) $lndir/bin/lndir -silent "$pkg/$origin_path" "$out/$dest_path";;
esac
fi
}
linkEmacsPackage() {
linkPath "$1" "bin" "bin"
linkPath "$1" "share/emacs/site-lisp" "share/emacs/site-lisp"
${lib.optionalString withNativeCompilation ''
linkPath "$1" "share/emacs/native-lisp" "share/emacs/native-lisp"
''}
${lib.optionalString withTreeSitter ''
linkPath "$1" "lib" "lib"
''}
}
# Iterate over the array of inputs (avoiding nix's own interpolation)
for pkg in "''${requires[@]}"; do
linkEmacsPackage $pkg
done
siteStart="$out/share/emacs/site-lisp/site-start.el"
siteStartByteCompiled="$siteStart"c
subdirs="$out/share/emacs/site-lisp/subdirs.el"
subdirsByteCompiled="$subdirs"c
# A dependency may have brought the original siteStart or subdirs, delete
# it and create our own
# Begin the new site-start.el by loading the original, which sets some
# NixOS-specific paths. Paths are searched in the reverse of the order
# they are specified in, so user and system profile paths are searched last.
#
# NOTE: Avoid displaying messages early at startup by binding
# inhibit-message to t. This would prevent the Emacs GUI from showing up
# prematurely. The messages would still be logged to the *Messages*
# buffer.
rm -f $siteStart $siteStartByteCompiled $subdirs $subdirsByteCompiled
cat >"$siteStart" <<EOF
;;; -*- lexical-binding: t -*-
(let ((inhibit-message t))
(load "$emacs/share/emacs/site-lisp/site-start"))
;; "$out/share/emacs/site-lisp" is added to load-path in wrapper.sh
;; "$out/share/emacs/native-lisp" is added to native-comp-eln-load-path in wrapper.sh
(add-to-list 'exec-path "$out/bin")
${lib.optionalString withTreeSitter ''
(add-to-list 'treesit-extra-load-path "$out/lib/")
''}
EOF
# Generate a subdirs.el that statically adds all subdirectories to load-path.
cat >"$subdirs" <<EOF
;;; -*- lexical-binding: t -*-
EOF
$emacs/bin/emacs \
--batch \
--load ${./mk-wrapper-subdirs.el} \
--eval "(prin1 (macroexpand-1 '(mk-subdirs-expr \"$out/share/emacs/site-lisp\")))" \
>> "$subdirs"
# Byte-compiling improves start-up time only slightly, but costs nothing.
$emacs/bin/emacs --batch -f batch-byte-compile "$siteStart" "$subdirs"
${lib.optionalString withNativeCompilation ''
$emacs/bin/emacs --batch \
--eval "(add-to-list 'native-comp-eln-load-path \"$out/share/emacs/native-lisp/\")" \
-f batch-native-compile "$siteStart" "$subdirs"
''}
'';
inherit (emacs) meta;
}
''
mkdir -p "$out/bin"
# Wrap emacs and friends so they find our site-start.el before the original.
for prog in $emacs/bin/*; do # */
local progname=$(basename "$prog")
rm -f "$out/bin/$progname"
substitute ${./wrapper.sh} $out/bin/$progname \
--subst-var-by bash ${emacs.stdenv.shell} \
--subst-var-by wrapperSiteLisp "$deps/share/emacs/site-lisp" \
--subst-var-by wrapperSiteLispNative "$deps/share/emacs/native-lisp" \
--subst-var-by wrapperInvocationDirectory "$out/bin/" \
--subst-var-by wrapperInvocationName "$progname" \
--subst-var prog
chmod +x $out/bin/$progname
# Create a “NOP” binary wrapper for the pure sake of it becoming a
# non-shebang, actual binary. See the makeBinaryWrapper docs for rationale
# (summary: it allows you to use emacs as a shebang itself on Darwin,
# e.g. #!$ {emacs}/bin/emacs --script)
wrapProgramBinary $out/bin/$progname
done
# Wrap MacOS app
# this has to pick up resources and metadata
# to recognize it as an "app"
if [ -d "$emacs/Applications/Emacs.app" ]; then
mkdir -p $out/Applications/Emacs.app/Contents/MacOS
cp -r $emacs/Applications/Emacs.app/Contents/Info.plist \
$emacs/Applications/Emacs.app/Contents/PkgInfo \
$emacs/Applications/Emacs.app/Contents/Resources \
$out/Applications/Emacs.app/Contents
substitute ${./wrapper.sh} $out/Applications/Emacs.app/Contents/MacOS/Emacs \
--subst-var-by bash ${emacs.stdenv.shell} \
--subst-var-by wrapperSiteLisp "$deps/share/emacs/site-lisp" \
--subst-var-by wrapperSiteLispNative "$deps/share/emacs/native-lisp" \
--subst-var-by wrapperInvocationDirectory "$out/Applications/Emacs.app/Contents/MacOS/" \
--subst-var-by wrapperInvocationName "Emacs" \
--subst-var-by prog "$emacs/Applications/Emacs.app/Contents/MacOS/Emacs"
chmod +x $out/Applications/Emacs.app/Contents/MacOS/Emacs
wrapProgramBinary $out/Applications/Emacs.app/Contents/MacOS/Emacs
fi
mkdir -p $out/share
# Link icons and desktop files into place
for dir in applications icons info man; do
ln -s $emacs/share/$dir $out/share/$dir
done
''