diff --git a/pkgs/applications/science/electronics/eagle/default.nix b/pkgs/applications/science/electronics/eagle/default.nix new file mode 100644 index 000000000000..04c9a9b79ec5 --- /dev/null +++ b/pkgs/applications/science/electronics/eagle/default.nix @@ -0,0 +1,86 @@ +{ stdenv, fetchurl, makeDesktopItem, patchelf, zlib, freetype, fontconfig +, openssl, libXrender, libXrandr, libXcursor, libX11, libXext, libXi +}: + +let + + libPath = stdenv.lib.makeLibraryPath + [ zlib freetype fontconfig openssl libXrender libXrandr libXcursor libX11 + libXext libXi + ]; + +in + +stdenv.mkDerivation rec { + name = "eagle-${version}"; + version = "6.4.0"; + + src = fetchurl { + url = "ftp://ftp.cadsoft.de/eagle/program/6.4/eagle-lin-${version}.run"; + sha256 = "0jb44dsq4cl9rx5nam6rxsw9fsmm6fsksv9s544p2zrwnad2x2i8"; + }; + + desktopItem = makeDesktopItem { + name = "Eagle"; + exec = "eagle"; + icon = "eagle"; + comment = "Schematic capture and PCB layout"; + desktopName = "Eagle"; + genericName = "Schematic editor"; + categories = "Application;Development;"; + }; + + buildInputs = + [ patchelf zlib freetype fontconfig openssl libXrender libXrandr libXcursor + libX11 libXext libXi + ]; + + phases = [ "installPhase" ]; + + # NOTES: + # Eagle for Linux comes as a self-extracting shell script with embedded + # tarball. The tarball data (.tar.bz2) starts after a __DATA__ marker. + # + # Eagle apparently doesn't like binary patching. This is what happens: + # $ ./result/eagle-6.4.0/bin/eagle + # argv[0] (/home/bfo/nixpkgs/result/eagle-6.4.0/bin/eagle) is not the currently executed program version! + installPhase = '' + # Extract eagle tarball + mkdir "$out" + sed '1,/^__DATA__$/d' "$src" | tar -xjf - -C "$out" + + # Install manpage + mkdir -p "$out"/share/man/man1 + ln -s "$out"/eagle-${version}/doc/eagle.1 "$out"/share/man/man1/eagle.1 + + # Build LD_PRELOAD library that redirects license file access to the home + # directory of the user + mkdir -p "$out"/lib + gcc -shared -fPIC -DEAGLE_PATH=\"$out/eagle-${version}\" ${./eagle_fixer.c} -o "$out"/lib/eagle_fixer.so -ldl + + # Make wrapper script + dynlinker="$(cat $NIX_GCC/nix-support/dynamic-linker)" + mkdir -p "$out"/bin + cat > "$out"/bin/eagle << EOF + #!${stdenv.shell} + export LD_LIBRARY_PATH="${stdenv.gcc.gcc}/lib:${libPath}" + export LD_PRELOAD="$out/lib/eagle_fixer.so" + exec "$dynlinker" "$out/eagle-${version}/bin/eagle" "\$@" + EOF + chmod a+x "$out"/bin/eagle + + # Make desktop item + mkdir -p "$out"/share/applications + cp "$desktopItem"/share/applications/* "$out"/share/applications/ + mkdir -p "$out"/share/icons + ln -s "$out/eagle-${version}/bin/eagleicon50.png" "$out"/share/icons/eagle.png + ''; + + meta = with stdenv.lib; { + description = "Schematic editor and PCB layout tool from CadSoft"; + homepage = http://www.cadsoftusa.com/; + license = licenses.unfree; + platforms = platforms.linux; + maintainers = [ maintainers.bjornfor ]; + }; +} diff --git a/pkgs/applications/science/electronics/eagle/eagle_fixer.c b/pkgs/applications/science/electronics/eagle/eagle_fixer.c new file mode 100644 index 000000000000..da9da4dcbd3a --- /dev/null +++ b/pkgs/applications/science/electronics/eagle/eagle_fixer.c @@ -0,0 +1,134 @@ +/* + * LD_PRELOAD trick to make Eagle (schematic editor and PCB layout tool from + * CadSoft) work from a read-only installation directory. + * + * When Eagle starts, it looks for the license file in /bin/eagle.key + * (where is the install path). If eagle.key is not found, Eagle checks + * for write access to /bin/, shows a license dialog to the user and + * then attempts to write a license file to /bin/. + * + * This will of course fail when Eagle is installed in the read-only Nix store. + * Hence this library that redirects accesses to the those paths in the + * following way: + * + * /bin => $HOME + * /bin/eagle.key => $HOME/.eagle.key + * + * Also, if copying an example project to ~/eagle/ (in the Eagle GUI), Eagle + * chmod's the destination with read-only permission bits (presumably because + * the source is read-only) and fails to complete the copy operation. + * Therefore, the mode argument in calls to chmod() is OR'ed with the S_IWUSR + * bit (write by owner). + * + * Usage: + * gcc -shared -fPIC -DEAGLE_PATH="$out/eagle-${version}" eagle_fixer.c -o eagle_fixer.so -ldl + * LD_PRELOAD=$PWD/eagle_fixer.so ./result/bin/eagle + * + * To see the paths that are modified at runtime, set the environment variable + * EAGLE_FIXER_DEBUG to 1. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef EAGLE_PATH +#error Missing EAGLE_PATH, path to the eagle-${version} installation directory. +#endif + +typedef FILE *(*fopen_func_t)(const char *path, const char *mode); +typedef int (*access_func_t)(const char *pathname, int mode); +typedef int (*chmod_func_t)(const char *path, mode_t mode); + +/* + * Map /bin to $HOME and /bin/eagle.key to $HOME/.eagle.key + * + * Path is truncated if bigger than PATH_MAX. It's not threadsafe, but that's + * OK. + */ +static const char *redirect(const char *pathname) +{ + static char buffer[PATH_MAX]; + const char *homepath; + const char *new_path; + static int have_warned; + + homepath = getenv("HOME"); + if (!homepath) { + homepath = "/"; + if (!have_warned && getenv("EAGLE_FIXER_DEBUG")) { + fprintf(stderr, "eagle_fixer: HOME is unset, using \"/\" (root) instead.\n"); + have_warned = 1; + } + } + + new_path = pathname; + if (strcmp(EAGLE_PATH "/bin", pathname) == 0) { + /* redirect to $HOME */ + new_path = homepath; + } else if (strcmp(EAGLE_PATH "/bin/eagle.key", pathname) == 0) { + /* redirect to $HOME/.eagle.key */ + snprintf(buffer, PATH_MAX, "%s/.eagle.key", homepath); + buffer[PATH_MAX-1] = '\0'; + new_path = buffer; + } + + return new_path; +} + +FILE *fopen(const char *pathname, const char *mode) +{ + FILE *fp; + const char *path; + fopen_func_t orig_fopen; + + orig_fopen = (fopen_func_t)dlsym(RTLD_NEXT, "fopen"); + path = redirect(pathname); + fp = orig_fopen(path, mode); + + if (path != pathname && getenv("EAGLE_FIXER_DEBUG")) { + fprintf(stderr, "eagle_fixer: fopen(\"%s\", \"%s\") => \"%s\": fp=%p\n", pathname, mode, path, fp); + } + + return fp; +} + +int access(const char *pathname, int mode) +{ + int ret; + const char *path; + access_func_t orig_access; + + orig_access = (access_func_t)dlsym(RTLD_NEXT, "access"); + path = redirect(pathname); + ret = orig_access(path, mode); + + if (path != pathname && getenv("EAGLE_FIXER_DEBUG")) { + fprintf(stderr, "eagle_fixer: access(\"%s\", %d) => \"%s\": ret=%d\n", pathname, mode, path, ret); + } + + return ret; +} + +int chmod(const char *pathname, mode_t mode) +{ + int ret; + mode_t new_mode; + chmod_func_t orig_chmod; + + orig_chmod = (chmod_func_t)dlsym(RTLD_NEXT, "chmod"); + new_mode = mode | S_IWUSR; + ret = orig_chmod(pathname, new_mode); + + if (getenv("EAGLE_FIXER_DEBUG")) { + fprintf(stderr, "eagle_fixer: chmod(\"%s\", %o) => %o: ret=%d\n", pathname, mode, new_mode, ret); + } + + return ret; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 7eec0aa05fb5..41ffa073a0a8 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -8971,6 +8971,8 @@ let ### SCIENCE / ELECTRONICS + eagle = callPackage_i686 ../applications/science/electronics/eagle { }; + caneda = callPackage ../applications/science/electronics/caneda { }; gtkwave = callPackage ../applications/science/electronics/gtkwave { };