nix-quartus/generic.nix
Ryan Orendorff 672449bce2 Add libXi dependency to enable Platform Designer
Platform Designer previously would not run due to not having libXi in
the library path. This patch adds the library to both the 32 bit and 64
bit versions of the package.
2020-10-22 21:44:59 -07:00

455 lines
19 KiB
Nix

{ stdenv, fetchurl, utillinux, file, bash, glibc, pkgsi686Linux, writeScript
, nukeReferences, glibcLocales, libfaketime, coreutils, gnugrep, gnused, proot
# Runtime dependencies
, zlib, glib, libpng12, freetype, libSM, libICE, libXrender, fontconfig
, libXext, libX11, libXtst, gtk2, bzip2, libelf, libXi
}:
{ baseName
, prettyName ? baseName
, version
, components ? []
, updateComponents ? []
# Set to true for the old installers that are 32-bit only
, is32bitPackage ? false
# There are .so files inside .jar files bundled with Quartus that lack RPATH
# directives. This breaks starting e.g. eclipse-nios2:
#
# java.lang.UnsatisfiedLinkError: Could not load SWT library. Reasons:
# $HOME/.altera.sbt4e/16.1.0.196-linux64/configuration/org.eclipse.osgi/bundles/404/1/.cp/libswt-pi-gtk-4335.so: libXtst.so.6: cannot open shared object file: No such file or directory
# no swt-pi-gtk in java.library.path
# $HOME/.swt/lib/linux/x86_64/libswt-pi-gtk-4335.so: libXtst.so.6: cannot open shared object file: No such file or directory
# Can't load library: $HOME/.swt/lib/linux/x86_64/libswt-pi-gtk.so
#
# Although we _could_ fixup these .so files that live inside .jar files (or
# find all java interpreters and specify "-Djava.library.path=" on their
# command line), I don't think it's worth it. Quartus *itself* sets
# LD_LIBRARY_PATH, thus breaking spawning firefox from it, so I see no reason
# we shouldn't be equally lazy.
, wrapWithLdLibraryPath ? true
}:
let
# Somewhere between NixOS 16.09 and 17.03 (for instance, commit 9e6eec201b)
# the glibc attribute lacked $out/lib{,64}. The glibc_lib attribute below
# helped when bisecting build issues between 16.03 and 17.03.
glibc_lib =
if glibc ? out then glibc.out else glibc;
glibc_lib32 =
if pkgsi686Linux.glibc ? out then pkgsi686Linux.glibc.out else pkgsi686Linux.glibc;
# Using glibc>=2.25 causes the Quartus*Setup*run installer to hang.
# Use 2.24 instead.
commonGlibcAttrs224 = rec {
name = "glibc-${version}";
version = "2.24";
src = fetchurl {
url = "http://ftpmirror.gnu.org/glibc/${name}.tar.xz";
sha256 = "1lxmprg9gm73gvafxd503x70z32phwjzcy74i0adfi6ixzla7m4r";
};
};
# Backport upstream patch to fix build against nixpkgs-18.09.
glibc_lib_for_installer = glibc_lib.overrideAttrs (oldAttrs:
commonGlibcAttrs224 // { patches = [ ./patches/glibc/0001-Avoid-.symver-on-common-symbols-BZ-21666.patch ]; }
);
glibc_lib32_for_installer = glibc_lib32.overrideAttrs (oldAttrs:
commonGlibcAttrs224 // { patches = [ ./patches/glibc/0001-Avoid-.symver-on-common-symbols-BZ-21666.patch ]; }
);
# Keep in sync with runtimeLibPath64
# (with pkgsi686Linux; [ .. ] doesn't bind strongly enough.)
runtimeLibPath32 =
stdenv.lib.makeLibraryPath
[ pkgsi686Linux.zlib pkgsi686Linux.glib pkgsi686Linux.libpng12
pkgsi686Linux.freetype pkgsi686Linux.xorg.libSM pkgsi686Linux.xorg.libICE
pkgsi686Linux.xorg.libXrender pkgsi686Linux.fontconfig.lib
pkgsi686Linux.xorg.libXext pkgsi686Linux.xorg.libX11 pkgsi686Linux.xorg.libXtst
pkgsi686Linux.gtk2 pkgsi686Linux.bzip2.out pkgsi686Linux.libelf
pkgsi686Linux.xorg.libXi
pkgsi686Linux.stdenv.cc.cc.lib
];
# Keep in sync with runtimeLibPath32
runtimeLibPath64 =
stdenv.lib.makeLibraryPath
[ zlib glib libpng12 freetype libSM libICE libXrender fontconfig.lib
libXext libX11 libXtst gtk2 bzip2.out libelf libXi
stdenv.cc.cc.lib
];
runtimeLibPath =
if is32bitPackage then runtimeLibPath32 else runtimeLibPath64;
runtimeBinPath = stdenv.lib.makeBinPath
[ coreutils gnugrep gnused glibc proot ];
setup-chroot-and-exec = writeScript "setup-chroot-and-exec"
(''
#!${bash}/bin/sh
chrootdir=chroot # relative to the current directory
mkdir -p "$chrootdir"/host
mkdir -p "$chrootdir"/proc
mkdir -p "$chrootdir"/nix
mkdir -p "$chrootdir"/tmp
mkdir -p "$chrootdir"/dev
mkdir -p "$chrootdir"/lib
mkdir -p "$chrootdir"/lib64
mkdir -p "$chrootdir"/bin
${utillinux}/bin/mount --rbind / "$chrootdir"/host
${utillinux}/bin/mount --rbind /proc "$chrootdir"/proc
${utillinux}/bin/mount --rbind /nix "$chrootdir"/nix
${utillinux}/bin/mount --rbind /tmp "$chrootdir"/tmp
${utillinux}/bin/mount --rbind /dev "$chrootdir"/dev
'' + (if is32bitPackage then ''
${utillinux}/bin/mount --rbind "${glibc_lib32_for_installer}"/lib "$chrootdir"/lib
'' else ''
${utillinux}/bin/mount --rbind "${glibc_lib_for_installer}"/lib64 "$chrootdir"/lib64
'') + ''
${utillinux}/bin/mount --rbind "${bash}"/bin "$chrootdir"/bin
chroot "$chrootdir" "$@"
'');
# buildFHSUserEnv from nixpkgs tries to mount a few directories that are not
# available in sandboxed Nix builds (/sys, /run), hence we have our own
# slimmed down variant.
run-in-fhs-env = writeScript "run-in-fhs-env"
''
#!${bash}/bin/sh
if [ "$*" = "" ]; then
echo "Usage: run-in-fhs-env <COMMAND> [ARGS...]"
exit 1
fi
"${utillinux}/bin/unshare" -r -U -m "${setup-chroot-and-exec}" "$@"
'';
mkInstallersDir = srcs:
stdenv.mkDerivation rec {
name = "${baseName}-installers";
inherit srcs version;
buildCommand =
''
# The files are copied, not symlinked, because
# - We must add execute bit to *.run files
# - Quartus*Setup*.run fails to use the *.qdz files if they are symlinks.
# Example error message (which doesn't abort the installer!):
# Error copying file from /nix/store/HASH1-altera-quartus-prime-lite-installers-16.1.0.196/cyclonev-16.1.0.196.qdz/cyclonev-16.1.0.196.qdz to /nix/store/HASH2-altera-quartus-prime-lite-16.1.0.196/cyclonev-16.1.0.196.qdz:
# /nix/store/HASH1-altera-quartus-prime-lite-installers-16.1.0.196/cyclonev-16.1.0.196.qdz/cyclonev-16.1.0.196.qdz does not exist
# Abort
# Unable to copy file
set -x
mkdir -p "$out"
${stdenv.lib.concatStringsSep "\n"
(map
(p: ''
cp "${p}" "$out/$(stripHash "${p}")"
'')
srcs
)
}
for f in $out/*run; do
[ -e "$f" ] && chmod +x "$f"
done
'';
};
componentInstallers = mkInstallersDir components;
updateComponentInstallers = mkInstallersDir updateComponents;
quartusUnwrapped = stdenv.mkDerivation rec {
name = "${baseName}-unwrapped-${version}";
inherit version;
# srcs is for keeping track of inputs used for the build.
srcs = components ++ updateComponents;
buildInputs = [ file nukeReferences ];
# Fix this:
# /nix/store/...-altera-quartus-ii-web-13.1.4.182/quartus/adm/qenv.sh: line 83: \
# warning: setlocale: LC_CTYPE: cannot change locale (en_US.UTF-8): No such file or directory
LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive";
# Prebuilt binaries need special treatment
dontStrip = true;
dontPatchELF = true;
# Fix "RPATH of binary X contains a forbidden reference to /build" issue.
# (These are prebuilt binaries, not much we can do.)
noAuditTmpdir = true;
configurePhase = "true";
buildPhase = "true";
unpackPhase = "true";
# Quartus' setup.sh (from the all-in-one-installers) doesn't fit our needs
# (we want automatic and distro-agnostic install), so call the actual setup
# program directly instead.
#
# Quartus*Setup*.run files are statically linked ELF executables that run
# open("/lib64/ld-linux-x86-64.so.2", ...) (or "/lib/ld-linux.so.2" for
# 32-bit versions) . That obviously doesn't work in sandboxed Nix builds.
#
# Things that do not work:
# * patchelf the installer (there is no .interp section in static ELF)
# * dynamic linker tricks (again, static ELF)
# * proot (the installer somehow detects something is wrong and aborts)
#
# We need bigger guns: user namespaces and chroot. That's how we make /lib64/
# available to the installer. The installer installs dynamically linked ELF
# files, so those we can fixup with usual tools.
#
# For runtime, injecting (or wrapping with) LD_LIBRARY_PATH is easier, but it
# messes with the environment for all child processes. We take the less
# invasive approach here, patchelf + RPATH. Unfortunately, Quartus itself
# uses LD_LIBRARY_PATH in its wrapper scripts. This cause e.g. firefox to
# fail due to LD_LIBRARY_PATH pulling in wrong libraries for it (happens if
# clicking any URL in Quartus).
installPhase = ''
set -x
run_quartus_installer()
{
installer="$1"
if [ ! -x "$installer" ]; then
echo "ERROR: \"$installer\" either doesn't exist or is not executable"
exit 1
fi
maybe_accept_eula="${if stdenv.lib.versionAtLeast version "17.1" then "--accept_eula 1" else ""}"
echo "### ${run-in-fhs-env} $installer --mode unattended --installdir $out" $maybe_accept_eula
"${run-in-fhs-env}" "$installer" --mode unattended --installdir "$out" $maybe_accept_eula
echo "...done"
}
echo "Running Quartus Setup (in FHS sandbox)..."
run_quartus_installer "$(echo "${componentInstallers}"/Quartus*Setup*)"
${stdenv.lib.optionalString (updateComponents != []) ''
echo "Running Quartus Update (in FHS sandbox)..."
run_quartus_installer "$(echo "${updateComponentInstallers}"/Quartus*Setup*)"
''}
echo "Removing unneeded \"uninstall\" binaries (saves $(du -sh "$out"/uninstall | cut -f1))"
rm -rf "$out"/uninstall
echo "Prevent retaining a runtime dependency on the installer binaries (saves $(du -sh "${componentInstallers}" | cut -f1) + $(du -sh "${updateComponentInstallers}" | cut -f1))"
nuke-refs "$out/logs/"*
echo "Fixing ELF interpreter paths with patchelf"
find "$out" -type f | while read f; do
case "$f" in
*.debug) continue;;
esac
# A few files are read-only. Make them writeable for patchelf. (Nix
# will make all files read-only after the build.)
chmod +w "$f"
magic=$(file "$f") || { echo "file \"$f\" failed"; exit 1; }
case "$magic" in
*ELF*dynamically\ linked*)
orig_rpath=$(patchelf --print-rpath "$f") || { echo "FAILED: patchelf --print-rpath $f"; exit 1; }
# Take care not to add ':' at start or end of RPATH, because
# that is the same as '.' (current directory), and that's
# insecure.
if [ "$orig_rpath" != "" ]; then
orig_rpath="$orig_rpath:"
fi
new_rpath="$orig_rpath${runtimeLibPath}"
case "$magic" in
*ELF*pie\ executable*|*ELF*shared\ object*x86-64*)
patchelf --set-rpath "$new_rpath" "$f" || { echo "FAILED: patchelf --set-rpath $f"; exit 1; }
;;
*ELF*executable*)
interp=$(patchelf --print-interpreter "$f") || { echo "FAILED: patchelf --print-interpreter $f"; exit 1; }
# Note the LSB interpreters, required by some files
case "$interp" in
/lib64/ld-linux-x86-64.so.2|/lib64/ld-lsb-x86-64.so.3)
new_interp=$(cat "$NIX_CC"/nix-support/dynamic-linker)
;;
/lib/ld-linux.so.2|/lib/ld-lsb.so.3)
new_interp="${glibc_lib32}/lib/ld-linux.so.2"
;;
/lib/ld-linux-armhf.so.3|/lib64/ld64.so.1|/lib64/ld64.so.2)
# Ignore ARM/ppc64/ppc64le executables, they
# are not meant to be run on the build machine.
# Example files:
# altera-quartus-prime-lite-15.1.0.185/hld/host/arm32/bin/aocl-binedit
# altera-quartus-prime-lite-15.1.0.185/hld/host/ppc64/bin/aocl-binedit
# altera-quartus-prime-lite-15.1.0.185/hld/host/ppc64le/bin/aocl-binedit
continue
;;
/usr/lib/ld.so.1)
echo "Unclear if this will work?"
new_interp="${glibc_lib32}/lib/ld-linux.so.2"
;;
*)
echo "FIXME: unhandled interpreter \"$interp\" in $f"
exit 1
;;
esac
test -f "$new_interp" || { echo "$new_interp is missing"; exit 1; }
patchelf --set-interpreter "$new_interp" \
--set-rpath "$new_rpath" "$f" || { echo "FAILED: patchelf --set-interpreter $new_interp --set-rpath $new_rpath $f"; exit 1; }
;;
esac
;;
*ELF*statically\ linked*)
echo "WARN: $f is statically linked. Needs fixup?"
;;
esac
done
# Modelsim is optional
f="$out"/modelsim_ase/vco
if [ -f "$f" ]; then
echo "Fix hardcoded \"/bin/ls\" in .../modelsim_ase/vco"
sed -i -e "s,/bin/ls,ls," "$f"
echo "Fix support for Linux 4.x in .../modelsim_ase/vco"
sed -i -e "/case \$utype in/a 4.[0-9]*) vco=\"linux\" ;;" "$f"
fi
'';
};
in
stdenv.mkDerivation rec {
name = "${baseName}-${version}";
# version and srcs are unused by this derivation, but keep them as metadata
# (for users).
inherit (quartusUnwrapped) version srcs;
buildCommand = ''
set -x
# Provide convenience wrappers in $out/bin, so that the tools can be
# started directly from PATH. Plain symlinks don't work, due to assumptions
# of resources relative to arg0.
wrap()
{
dest="$out/bin/$(basename "$1")"
if [ -f "$dest" ]; then
echo "ERROR: $dest already exist"
exit 1
fi
cat > "$dest" << EOF
#!${bash}/bin/sh
# Some tools seem to forget sourcing their environment setup file (e.g
# elf2hex), so help them by setting QUARTUS_ROOTDIR (and *OVERRIDE).
# (Perhaps these tools were never tested _not_ being started from Quartus
# IDE?)
export QUARTUS_ROOTDIR="${quartusUnwrapped}/quartus"
export QUARTUS_ROOTDIR_OVERRIDE="\$QUARTUS_ROOTDIR"
# To prevent e.g. "alt-file-convert" from aborting due to not being able to
# write to __pycache__ in the (read-only) nix store.
export PYTHONDONTWRITEBYTECODE=1
${stdenv.lib.optionalString wrapWithLdLibraryPath ''
if [ "x\$LD_LIBRARY_PATH" != x ]; then
export LD_LIBRARY_PATH="${runtimeLibPath}:\$LD_LIBRARY_PATH"
else
export LD_LIBRARY_PATH="${runtimeLibPath}"
fi
''}
# Inject path to known tools. This is important not only for having a
# complete package (all deps), but also to ensure that LD_PRELOAD etc.
# won't break the CLI tools. (Think non-NixOS setups.)
export PATH="${runtimeBinPath}:\$PATH"
# Check if we need to isolate ourself from /bin/sh. This is to work around
# a long standing bug in nixpkgs that glibc system() calls /bin/sh. The
# reasoning behind the test is that Nix tools don't depend on things in
# /lib. The test should be positive only on non-sandboxed, non-NixOS builds.
bin_sh_is_clean=1
if ldd /bin/sh | grep -q "^\s*/lib"; then
bin_sh_is_clean=0
fi
# Implement the SOURCE_DATE_EPOCH specification, for reproducible builds:
# https://reproducible-builds.org/specs/source-date-epoch
if [ "x\$SOURCE_DATE_EPOCH" != x ]; then
# Create a minimal "sandbox" with proot, or else programs running
# /bin/sh will fail because we're setting LD_PRELOAD etc.
if [ \$bin_sh_is_clean -eq 0 ]; then
maybe_proot_cmd="proot -b ${bash}/bin/sh:/bin/sh"
# Work around bug with linux 4.8.4+ (costs some performance, but
# prevents breakage).
export PROOT_NO_SECCOMP=1
fi
# Prepare LD_LIBRARY_PATH, LD_PRELOAD
if [ "x\$LD_LIBRARY_PATH" != x ]; then
export LD_LIBRARY_PATH="${libfaketime}/lib:\$LD_LIBRARY_PATH"
else
export LD_LIBRARY_PATH="${libfaketime}/lib"
fi
if [ "x${toString is32bitPackage}" = "x${toString true}" ]; then
export LD_LIBRARY_PATH="${pkgsi686Linux.libfaketime}/lib:\$LD_LIBRARY_PATH"
fi
if [ "x\$LD_PRELOAD" != x ]; then
export LD_PRELOAD="libfaketime.so.1:\$LD_PRELOAD"
else
export LD_PRELOAD=libfaketime.so.1
fi
# Set the time to SOURCE_DATE_EPOCH
export FAKETIME_FMT="%s"
export FAKETIME=\$(date +%s -d @\$SOURCE_DATE_EPOCH)
fi
# Fix this:
# /nix/store/...-altera-quartus-ii-web-13.1.4.182/quartus/adm/qenv.sh: line 83: \
# warning: setlocale: LC_CTYPE: cannot change locale (en_US.UTF-8): No such file or directory
export LOCALE_ARCHIVE="${glibcLocales}/lib/locale/locale-archive"
exec \$maybe_proot_cmd "$1" "\$@"
EOF
chmod +x "$dest"
}
echo "Creating top-level bin/ directory with wrappers for common tools"
mkdir -p "$out/bin"
for p in "${quartusUnwrapped}/"*"/bin/"*; do
test -f "$p" || continue
wrap "$p"
done
echo "Installing Desktop file..."
mkdir -p "$out/share/applications"
f="$out"/share/applications/quartus.desktop
cat >> "$f" << EOF
[Desktop Entry]
Type=Application
Name=${prettyName} ${version}
Comment=${prettyName} ${version}
Icon=${quartusUnwrapped}/quartus/adm/quartusii.png
Exec=$out/bin/quartus
Terminal=false
Path=$out
EOF
# udev rules based on
# https://www.intel.com/content/www/us/en/programmable/support/support-resources/download/drivers/dri-usb_b-lnx.html
echo "Installing udev rules"
mkdir -p "$out/lib/udev/rules.d"
cat > "$out/lib/udev/rules.d/51-usbblaster.rules" << EOF
# USB-Blaster / Intel FPGA Download Cable
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6001", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6002", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6003", MODE="0666"
# USB-Blaster II / Intel FPGA Download Cable II
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6010", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6810", MODE="0666"
EOF
'';
meta = with stdenv.lib; {
description = "Development tools for Altera FPGA, CPLD and SoC designs";
homepage = https://www.altera.com/;
license = licenses.unfree;
platforms = [ "x86_64-linux" ];
maintainers = [ maintainers.bjornfor ];
};
}