about summary refs log tree commit diff
diff options
context:
space:
mode:
authorKarl Hallsby <karl@hallsby.com>2021-02-14 00:27:17 -0600
committerDoron Behar <doron.behar@gmail.com>2021-02-24 21:00:48 +0200
commit481e1d3a77e1933d82fc66b78f7b2b42fdce91d7 (patch)
treec2cb2854998d8eb75b56109d857aa001c95d1cde
parent148eff94b383b5b0bd247e32d03a7d224e142598 (diff)
octave.pkgs: init
Heavily based on Python's packages set.
-rw-r--r--pkgs/development/interpreters/octave/build-env.nix83
-rw-r--r--pkgs/development/interpreters/octave/build-octave-package.nix113
-rw-r--r--pkgs/development/interpreters/octave/default.nix231
-rw-r--r--pkgs/development/interpreters/octave/hooks/default.nix13
-rw-r--r--pkgs/development/interpreters/octave/hooks/octave-write-required-octave-packages-hook.sh17
-rw-r--r--pkgs/development/interpreters/octave/hooks/write-required-octave-packages-hook.sh17
-rw-r--r--pkgs/development/interpreters/octave/with-packages.nix6
-rw-r--r--pkgs/development/interpreters/octave/wrap-octave.nix16
-rw-r--r--pkgs/development/interpreters/octave/wrap.sh132
-rw-r--r--pkgs/top-level/all-packages.nix2
-rw-r--r--pkgs/top-level/octave-packages.nix115
11 files changed, 646 insertions, 99 deletions
diff --git a/pkgs/development/interpreters/octave/build-env.nix b/pkgs/development/interpreters/octave/build-env.nix
new file mode 100644
index 0000000000000..8eb70c6289457
--- /dev/null
+++ b/pkgs/development/interpreters/octave/build-env.nix
@@ -0,0 +1,83 @@
+{ stdenv, octave, buildEnv
+, makeWrapper, texinfo
+, octavePackages
+, wrapOctave
+, computeRequiredOctavePackages
+, extraLibs ? []
+, extraOutputsToInstall ? []
+, postBuild ? ""
+, ignoreCollisions ? false
+}:
+
+# Create an octave executable that knows about additional packages
+let
+  packages = computeRequiredOctavePackages extraLibs;
+
+in buildEnv {
+  name = "${octave.name}-env";
+  paths = extraLibs ++ [ octave ];
+
+  inherit ignoreCollisions;
+  extraOutputsToInstall = [ "out" ] ++ extraOutputsToInstall;
+
+  buildInputs = [ makeWrapper texinfo wrapOctave ];
+
+  # During "build" we must first unlink the /share symlink to octave's /share
+  # Then, we can re-symlink the all of octave/share, except for /share/octave
+  # in env/share/octave, re-symlink everything from octave/share/octave and then
+  # perform the pkg install.
+  postBuild = ''
+      . "${makeWrapper}/nix-support/setup-hook"
+      # The `makeWrapper` used here is the one defined in
+      # ${makeWrapper}/nix-support/setup-hook
+
+      if [ -L "$out/bin" ]; then
+         unlink $out/bin
+         mkdir -p "$out/bin"
+         cd "${octave}/bin"
+         for prg in *; do
+             if [ -x $prg ]; then
+                makeWrapper "${octave}/bin/$prg" "$out/bin/$prg" --set OCTAVE_SITE_INITFILE "$out/share/octave/site/m/startup/octaverc"
+             fi
+         done
+         cd $out
+      fi
+
+      # Remove symlinks to the input tarballs, they aren't needed.
+      rm $out/*.tar.gz
+
+      createOctavePackagesPath $out ${octave}
+
+      for path in ${stdenv.lib.concatStringsSep " " packages}; do
+          if [ -e $path/*.tar.gz ]; then
+             $out/bin/octave-cli --eval "pkg local_list $out/.octave_packages; \
+                                         pkg prefix $out/${octave.octPkgsPath} $out/${octave.octPkgsPath}; \
+                                         pfx = pkg (\"prefix\"); \
+                                         pkg install -nodeps -local $path/*.tar.gz"
+          fi
+      done
+
+      # Re-write the octave-wide startup file (share/octave/site/m/startup/octaverc)
+      # To point to the new local_list in $out
+      addPkgLocalList $out ${octave}
+
+      wrapOctavePrograms "${stdenv.lib.concatStringsSep " " packages}"
+     '' + postBuild;
+
+  inherit (octave) meta;
+
+  passthru = octave.passthru // {
+    interpreter = "$out/bin/octave";
+    inherit octave;
+    env = stdenv.mkDerivation {
+      name = "interactive-${octave.name}-environment";
+
+      buildCommand = ''
+        echo >&2 ""
+        echo >&2 "*** octave 'env' attributes are intended for interactive nix-shell sessions, not for building! ***"
+        echo >&2 ""
+        exit 1
+      '';
+    };
+  };
+}
diff --git a/pkgs/development/interpreters/octave/build-octave-package.nix b/pkgs/development/interpreters/octave/build-octave-package.nix
new file mode 100644
index 0000000000000..73a67769d6a6e
--- /dev/null
+++ b/pkgs/development/interpreters/octave/build-octave-package.nix
@@ -0,0 +1,113 @@
+# Generic builder for GNU Octave libraries.
+# This is a file that contains nested functions. The first, outer, function
+# is the library- and package-wide details, such as the nixpkgs library, any
+# additional configuration provided, and the namePrefix to use (based on the
+# pname and version of Octave), the octave package, etc.
+
+{ lib
+, stdenv
+, config
+, octave
+, texinfo
+, computeRequiredOctavePackages
+, writeRequiredOctavePackagesHook
+}:
+
+# The inner function contains information required to build the individual
+# libraries.
+{ fullLibName ? "${attrs.pname}-${attrs.version}"
+
+, src
+
+, dontPatch ? false
+, patches ? []
+, patchPhase ? ""
+
+, enableParallelBuilding ? true
+# Build-time dependencies for the package, which were compiled for the system compiling this.
+, nativeBuildInputs ? []
+
+# Build-time dependencies for the package, which may not have been compiled for the system compiling this.
+, buildInputs ? []
+
+# Propagate build dependencies so in case we have A -> B -> C,
+# C can import package A propagated by B
+# Run-time dependencies for the package.
+, propagatedBuildInputs ? []
+
+# Octave packages that are required at runtime for this one.
+# These behave similarly to propagatedBuildInputs, where if
+# package A is needed by B, and C needs B, then C also requires A.
+# The main difference between these and propagatedBuildInputs is
+# during the package's installation into octave, where all
+# requiredOctavePackages are ALSO installed into octave.
+, requiredOctavePackages ? []
+
+, preBuild ? ""
+
+, meta ? {}
+
+, passthru ? {}
+
+, ... } @ attrs:
+
+let
+  requiredOctavePackages' = computeRequiredOctavePackages requiredOctavePackages;
+
+in stdenv.mkDerivation {
+  packageName = "${fullLibName}";
+  # The name of the octave package ends up being
+  # "octave-version-package-version"
+  name = "${octave.pname}-${octave.version}-${fullLibName}";
+
+  # This states that any package built with the function that this returns
+  # will be an octave package. This is used for ensuring other octave
+  # packages are installed into octave during the environment building phase.
+  isOctavePackage = true;
+
+  OCTAVE_HISTFILE = "/dev/null";
+
+  inherit src;
+
+  inherit dontPatch patches patchPhase;
+
+  dontConfigure = true;
+
+  enableParallelBuilding = enableParallelBuilding;
+
+  requiredOctavePackages = requiredOctavePackages';
+
+  nativeBuildInputs = [
+    octave
+    writeRequiredOctavePackagesHook
+  ]
+  ++ nativeBuildInputs;
+
+  buildInputs = buildInputs ++ requiredOctavePackages';
+
+  propagatedBuildInputs = propagatedBuildInputs ++ [ texinfo ];
+
+  preBuild = if preBuild == "" then
+    ''
+      # This trickery is needed because Octave expects a single directory inside
+      # at the top-most level of the tarball.
+      tar --transform 's,^,${fullLibName}/,' -cz * -f ${fullLibName}.tar.gz
+    ''
+             else
+               preBuild;
+
+  buildPhase = ''
+    runHook preBuild
+
+    mkdir -p $out
+    octave-cli --eval "pkg build $out ${fullLibName}.tar.gz"
+
+    runHook postBuild
+  '';
+
+  # We don't install here, because that's handled when we build the environment
+  # together with Octave.
+  dontInstall = true;
+
+  inherit meta;
+}
diff --git a/pkgs/development/interpreters/octave/default.nix b/pkgs/development/interpreters/octave/default.nix
index 72f3dd552de6d..0a87c1ddcf07f 100644
--- a/pkgs/development/interpreters/octave/default.nix
+++ b/pkgs/development/interpreters/octave/default.nix
@@ -1,4 +1,5 @@
 { stdenv
+, pkgs
 , lib
 # Note: either stdenv.mkDerivation or, for octaveFull, the qt-5 mkDerivation
 # with wrapQtAppsHook (comes from libsForQt5.callPackage)
@@ -45,6 +46,11 @@
 , python ? null
 , overridePlatforms ? null
 , sundials ? null
+# - Packages required for building extra packages.
+, newScope
+, callPackage
+, makeSetupHook
+, makeWrapper
 # - Build Octave Qt GUI:
 , enableQt ? false
 , qtbase ? null
@@ -60,6 +66,7 @@
 }:
 
 let
+
   # Not always evaluated
   blas' = if use64BitIdx then
     blas.override {
@@ -94,118 +101,144 @@ let
   else
     null
   ;
-in mkDerivation rec {
-  version = "6.2.0";
-  pname = "octave";
 
-  src = fetchurl {
-    url = "mirror://gnu/octave/${pname}-${version}.tar.gz";
-    sha256 = "sha256-RX0f2oY0qDni/Xz8VbmL1W82tq5z0xu530Pd4wEsqnw=";
+  octavePackages = import ../../../top-level/octave-packages.nix {
+    inherit pkgs;
+    inherit lib stdenv fetchurl newScope;
+    octave = self;
   };
 
-  buildInputs = [
-    readline
-    ncurses
-    perl
-    flex
-    qhull
-    graphicsmagick
-    pcre
-    fltk
-    zlib
-    curl
-    blas'
-    lapack'
-    libsndfile
-    fftw
-    fftwSinglePrec
-    portaudio
-    qrupdate'
-    arpack'
-    libwebp
-    gl2ps
-  ]
-  ++ lib.optionals enableQt [
-    qtbase
-    qtsvg
-    qscintilla
-  ]
-  ++ lib.optionals (ghostscript != null) [ ghostscript ]
-  ++ lib.optionals (hdf5 != null) [ hdf5 ]
-  ++ lib.optionals (glpk != null) [ glpk ]
-  ++ lib.optionals (suitesparse != null) [ suitesparse' ]
-  ++ lib.optionals (enableJava) [ jdk ]
-  ++ lib.optionals (sundials != null) [ sundials ]
-  ++ lib.optionals (gnuplot != null) [ gnuplot ]
-  ++ lib.optionals (python != null) [ python ]
-  ++ lib.optionals (!stdenv.isDarwin) [ libGL libGLU libX11 ]
-  ++ lib.optionals stdenv.isDarwin [
-    libiconv
-    darwin.apple_sdk.frameworks.Accelerate
-    darwin.apple_sdk.frameworks.Cocoa
-  ]
-  ;
-  nativeBuildInputs = [
-    pkg-config
-    gfortran
-    # Listed here as well because it's outputs are split
-    fftw
-    fftwSinglePrec
-    texinfo
-  ]
-  ++ lib.optionals (sundials != null) [ sundials ]
-  ++ lib.optionals enableJIT [ llvm ]
-  ++ lib.optionals enableQt [
-    qtscript
-    qttools
-  ]
-  ;
+  wrapOctave = callPackage ./wrap-octave.nix {
+    octave = self;
+    inherit (pkgs) makeSetupHook makeWrapper;
+  };
+
+  self = mkDerivation rec {
+    version = "6.2.0";
+    pname = "octave";
+
+    src = fetchurl {
+      url = "mirror://gnu/octave/${pname}-${version}.tar.gz";
+      sha256 = "sha256-RX0f2oY0qDni/Xz8VbmL1W82tq5z0xu530Pd4wEsqnw=";
+    };
+
+    buildInputs = [
+      readline
+      ncurses
+      perl
+      flex
+      qhull
+      graphicsmagick
+      pcre
+      fltk
+      zlib
+      curl
+      blas'
+      lapack'
+      libsndfile
+      fftw
+      fftwSinglePrec
+      portaudio
+      qrupdate'
+      arpack'
+      libwebp
+      gl2ps
+    ]
+    ++ lib.optionals enableQt [
+      qtbase
+      qtsvg
+      qscintilla
+    ]
+    ++ lib.optionals (ghostscript != null) [ ghostscript ]
+    ++ lib.optionals (hdf5 != null) [ hdf5 ]
+    ++ lib.optionals (glpk != null) [ glpk ]
+    ++ lib.optionals (suitesparse != null) [ suitesparse' ]
+    ++ lib.optionals (enableJava) [ jdk ]
+    ++ lib.optionals (sundials != null) [ sundials ]
+    ++ lib.optionals (gnuplot != null) [ gnuplot ]
+    ++ lib.optionals (python != null) [ python ]
+    ++ lib.optionals (!stdenv.isDarwin) [ libGL libGLU libX11 ]
+    ++ lib.optionals stdenv.isDarwin [
+      libiconv
+      darwin.apple_sdk.frameworks.Accelerate
+      darwin.apple_sdk.frameworks.Cocoa
+    ]
+    ;
+    nativeBuildInputs = [
+      pkg-config
+      gfortran
+      # Listed here as well because it's outputs are split
+      fftw
+      fftwSinglePrec
+      texinfo
+    ]
+    ++ lib.optionals (sundials != null) [ sundials ]
+    ++ lib.optionals enableJIT [ llvm ]
+    ++ lib.optionals enableQt [
+      qtscript
+      qttools
+    ]
+    ;
 
-  doCheck = !stdenv.isDarwin;
+    doCheck = !stdenv.isDarwin;
 
-  enableParallelBuilding = true;
+    enableParallelBuilding = true;
 
-  # See https://savannah.gnu.org/bugs/?50339
-  F77_INTEGER_8_FLAG = if use64BitIdx then "-fdefault-integer-8" else "";
+    # See https://savannah.gnu.org/bugs/?50339
+    F77_INTEGER_8_FLAG = if use64BitIdx then "-fdefault-integer-8" else "";
 
-  configureFlags = [
-    "--with-blas=blas"
-    "--with-lapack=lapack"
-    (if use64BitIdx then "--enable-64" else "--disable-64")
-  ]
+    configureFlags = [
+      "--with-blas=blas"
+      "--with-lapack=lapack"
+      (if use64BitIdx then "--enable-64" else "--disable-64")
+    ]
     ++ lib.optionals stdenv.isDarwin [ "--enable-link-all-dependencies" ]
     ++ lib.optionals enableReadline [ "--enable-readline" ]
     ++ lib.optionals stdenv.isDarwin [ "--with-x=no" ]
     ++ lib.optionals enableQt [ "--with-qt=5" ]
     ++ lib.optionals enableJIT [ "--enable-jit" ]
-  ;
+    ;
 
-  # Keep a copy of the octave tests detailed results in the output
-  # derivation, because someone may care
-  postInstall = ''
-    cp test/fntests.log $out/share/octave/${pname}-${version}-fntests.log || true
-  '';
+    # Keep a copy of the octave tests detailed results in the output
+    # derivation, because someone may care
+    postInstall = ''
+      cp test/fntests.log $out/share/octave/${pname}-${version}-fntests.log || true
+    '';
 
-  passthru = {
-    sitePath = "share/octave/${version}/site";
-    blas = blas';
-    lapack = lapack';
-    qrupdate = qrupdate';
-    arpack = arpack';
-    suitesparse = suitesparse';
-    inherit python;
-    inherit enableQt enableJIT enableReadline enableJava;
-  };
+    passthru = rec {
+      sitePath = "share/octave/${version}/site";
+      octPkgsPath = "share/octave/octave_packages";
+      blas = blas';
+      lapack = lapack';
+      qrupdate = qrupdate';
+      arpack = arpack';
+      suitesparse = suitesparse';
+      inherit fftw fftwSinglePrec;
+      inherit portaudio;
+      inherit jdk;
+      inherit python;
+      inherit enableQt enableJIT enableReadline enableJava;
+      buildEnv = callPackage ./build-env.nix {
+        octave = self;
+        inherit octavePackages wrapOctave;
+        inherit (octavePackages) computeRequiredOctavePackages;
+      };
+      withPackages = import ./with-packages.nix { inherit buildEnv octavePackages; };
+      pkgs = octavePackages;
+      interpreter = "${self}/bin/octave";
+    };
 
-  meta = {
-    homepage = "https://www.gnu.org/software/octave/";
-    license = lib.licenses.gpl3Plus;
-    maintainers = with lib.maintainers; [ raskin doronbehar ];
-    description = "Scientific Pragramming Language";
-    # https://savannah.gnu.org/bugs/?func=detailitem&item_id=56425 is the best attempt to fix JIT
-    broken = enableJIT;
-    platforms = if overridePlatforms == null then
-      (lib.platforms.linux ++ lib.platforms.darwin)
-    else overridePlatforms;
+    meta = {
+      homepage = "https://www.gnu.org/software/octave/";
+      license = lib.licenses.gpl3Plus;
+      maintainers = with lib.maintainers; [ raskin doronbehar ];
+      description = "Scientific Pragramming Language";
+      # https://savannah.gnu.org/bugs/?func=detailitem&item_id=56425 is the best attempt to fix JIT
+      broken = enableJIT;
+      platforms = if overridePlatforms == null then
+        (lib.platforms.linux ++ lib.platforms.darwin)
+      else overridePlatforms;
+    };
   };
-}
+
+in self
diff --git a/pkgs/development/interpreters/octave/hooks/default.nix b/pkgs/development/interpreters/octave/hooks/default.nix
new file mode 100644
index 0000000000000..f47560921af3c
--- /dev/null
+++ b/pkgs/development/interpreters/octave/hooks/default.nix
@@ -0,0 +1,13 @@
+# Hooks for building Octave packages.
+{ octave
+, lib
+, callPackage
+, makeSetupHook
+}:
+
+rec {
+  writeRequiredOctavePackagesHook = callPackage ({ }:
+    makeSetupHook {
+      name = "write-required-octave-packages-hook";
+    } ./write-required-octave-packages-hook.sh) {};
+}
diff --git a/pkgs/development/interpreters/octave/hooks/octave-write-required-octave-packages-hook.sh b/pkgs/development/interpreters/octave/hooks/octave-write-required-octave-packages-hook.sh
new file mode 100644
index 0000000000000..64e87d68246f1
--- /dev/null
+++ b/pkgs/development/interpreters/octave/hooks/octave-write-required-octave-packages-hook.sh
@@ -0,0 +1,17 @@
+# Setup hook for writing octave packages that are run-time dependencies for
+# another package to a nix-support file.
+# `echo`s the full path name to the package derivation that is required.
+echo "Sourcing octave-write-required-octave-packages-hook.sh"
+
+octaveWriteRequiredOctavePackagesPhase() {
+    echo "Executing octaveWriteRequiredOctavePackagesPhase"
+
+    mkdir -p $out/nix-support
+    echo ${requiredOctavePackages} > $out/nix-support/required-octave-packages
+}
+
+# Yes its a bit long...
+if [ -z "${dontWriteRequiredOctavePackagesPhase-}" ]; then
+    echo "Using octaveWriteRequiredOctavePackagesPhase"
+    preDistPhases+=" octaveWriteRequiredOctavePackagesPhase"
+fi
diff --git a/pkgs/development/interpreters/octave/hooks/write-required-octave-packages-hook.sh b/pkgs/development/interpreters/octave/hooks/write-required-octave-packages-hook.sh
new file mode 100644
index 0000000000000..032ea398ac568
--- /dev/null
+++ b/pkgs/development/interpreters/octave/hooks/write-required-octave-packages-hook.sh
@@ -0,0 +1,17 @@
+# Setup hook for writing octave packages that are run-time dependencies for
+# another package to a nix-support file.
+# `echo`s the full path name to the package derivation that is required.
+echo "Sourcing write-required-octave-packages-hook.sh"
+
+writeRequiredOctavePackagesPhase() {
+    echo "Executing writeRequiredOctavePackagesPhase"
+
+    mkdir -p $out/nix-support
+    echo ${requiredOctavePackages} > $out/nix-support/required-octave-packages
+}
+
+# Yes its a bit long...
+if [ -z "${dontWriteRequiredOctavePackagesPhase-}" ]; then
+    echo "Using writeRequiredOctavePackagesPhase"
+    preDistPhases+=" writeRequiredOctavePackagesPhase"
+fi
diff --git a/pkgs/development/interpreters/octave/with-packages.nix b/pkgs/development/interpreters/octave/with-packages.nix
new file mode 100644
index 0000000000000..f00befbb00d45
--- /dev/null
+++ b/pkgs/development/interpreters/octave/with-packages.nix
@@ -0,0 +1,6 @@
+{ buildEnv, octavePackages }:
+
+# Takes the buildEnv defined for Octave and the set of octavePackages, and returns
+# a function, which when given a function whose return value is a list of extra
+# packages to install, builds and returns that environment.
+f: let packages = f octavePackages; in buildEnv.override { extraLibs = packages; }
diff --git a/pkgs/development/interpreters/octave/wrap-octave.nix b/pkgs/development/interpreters/octave/wrap-octave.nix
new file mode 100644
index 0000000000000..1e4616136a1b9
--- /dev/null
+++ b/pkgs/development/interpreters/octave/wrap-octave.nix
@@ -0,0 +1,16 @@
+{ lib
+, octave
+, makeSetupHook
+, makeWrapper
+}:
+
+# Defined in trivial-builders.nix
+# Imported as wrapOctave in octave/default.nix and passed to octave's buildEnv
+# as nativeBuildInput
+# Each of the substitutions is available in the wrap.sh script as @thingSubstituted@
+makeSetupHook {
+  name = "${octave.name}-pkgs-setup-hook";
+  deps = makeWrapper;
+  substitutions.executable = octave.interpreter;
+  substitutions.octave = octave;
+} ./wrap.sh
diff --git a/pkgs/development/interpreters/octave/wrap.sh b/pkgs/development/interpreters/octave/wrap.sh
new file mode 100644
index 0000000000000..a5969fca2a96e
--- /dev/null
+++ b/pkgs/development/interpreters/octave/wrap.sh
@@ -0,0 +1,132 @@
+# Unlinks a directory (given as the first argument), and re-creates that
+# directory as an actual directory. Then descends into the directory of
+# the same name in the origin (arg_2/arg_3) and symlinks the contents of
+# that directory into the passed end-location.
+unlinkDirReSymlinkContents() {
+    local dirToUnlink="$1"
+    local origin="$2"
+    local contentsLocation="$3"
+
+    unlink $dirToUnlink/$contentsLocation
+    mkdir -p $dirToUnlink/$contentsLocation
+    for f in $origin/$contentsLocation/*; do
+        ln -s -t "$dirToUnlink/$contentsLocation" "$f"
+    done
+}
+
+# Using unlinkDirReSymlinkContents, un-symlinks directories down to
+# $out/share/octave, and then creates the octave_packages directory.
+createOctavePackagesPath() {
+    local desiredOut=$1
+    local origin=$2
+
+    if [ -L "$out/share" ]; then
+        unlinkDirReSymlinkContents "$desiredOut" "$origin" "share"
+    fi
+
+    if [ -L "$out/share/octave" ]; then
+        unlinkDirReSymlinkContents "$desiredOut" "$origin" "share/octave"
+    fi
+
+    # Now that octave_packages has a path rather than symlinks, create the
+    # octave_packages directory for installed packages.
+    mkdir -p "$desiredOut/share/octave/octave_packages"
+}
+
+# First, descends down to $out/share/octave/site/m/startup/octaverc, and
+# copies that start-up file. Once done, it performs a `chmod` to allow
+# writing. Lastly, it `echo`s the location of the locally installed packages
+# to the startup file, allowing octave to discover installed packages.
+addPkgLocalList() {
+    local desiredOut=$1
+    local origin=$2
+    local octaveSite="share/octave/site"
+    local octaveSiteM="$octaveSite/m"
+    local octaveSiteStartup="$octaveSiteM/startup"
+    local siteOctavercStartup="$octaveSiteStartup/octaverc"
+
+    unlinkDirReSymlinkContents "$desiredOut" "$origin" "$octaveSite"
+    unlinkDirReSymlinkContents "$desiredOut" "$origin" "$octaveSiteM"
+    unlinkDirReSymlinkContents "$desiredOut" "$origin" "$octaveSiteStartup"
+
+    unlink "$out/$siteOctavercStartup"
+    cp "$origin/$siteOctavercStartup" "$desiredOut/$siteOctavercStartup"
+    chmod u+w "$desiredOut/$siteOctavercStartup"
+    echo "pkg local_list $out/.octave_packages" >> "$desiredOut/$siteOctavercStartup"
+}
+
+# Wrapper function for wrapOctaveProgramsIn. Takes one argument, a
+# space-delimited string of packages' paths that will be installed.
+wrapOctavePrograms() {
+    wrapOctaveProgramsIn "$out/bin" "$out" "$@"
+}
+
+# Wraps all octave programs in $out/bin with all the propagated inputs that
+# a particular package requires. $1 is the directory to look for binaries in
+# to wrap. $2 is the path to the octave ENVIRONMENT. $3 is the space-delimited
+# string of packages.
+wrapOctaveProgramsIn() {
+    local dir="$1"
+    local octavePath="$2"
+    local pkgs="$3"
+    local f
+
+    buildOctavePath "$octavePath" "$pkgs"
+
+    # Find all regular files in the output directory that are executable.
+    if [ -d "$dir" ]; then
+        find "$dir" -type f -perm -0100 -print0 | while read -d "" f; do
+            echo "wrapping \`$f'..."
+            local -a wrap_args=("$f"
+                --prefix PATH ':' "$program_PATH"
+                   )
+            local -a wrapProgramArgs=("${wrap_args[@]}")
+            wrapProgram "${wrapProgramArgs[@]}"
+    done
+    fi
+}
+
+# Build the PATH environment variable by walking through the closure of
+# dependencies. Starts by constructing the `program_PATH` variable with the
+# environment's path, then adding the original octave's location, and marking
+# them in `octavePathsSeen`.
+buildOctavePath() {
+    local octavePath="$1"
+    local packages="$2"
+
+    local pathsToSearch="$octavePath $packages"
+
+    # Create an empty table of Octave paths.
+    declare -A octavePathsSeen=()
+    program_PATH=
+    octavePathsSeen["$out"]=1
+    octavePathsSeen["@octave@"]=1
+    addToSearchPath program_PATH "$out/bin"
+    addToSearchPath program_PATH "@octave@/bin"
+    echo "program_PATH to change to is: $program_PATH"
+    for path in $pathsToSearch; do
+    echo "Recurse to propagated-build-input: $path"
+    _addToOctavePath $path
+    done
+}
+
+# Adds the bin directories to the program_PATH variable.
+# Recurses on any paths declared in `propagated-build-inputs`, while avoiding
+# duplicating paths by flagging the directires it has seen in `octavePathsSeen`.
+_addToOctavePath() {
+    local dir="$1"
+    # Stop if we've already visited this path.
+    if [ -n "${octavePathsSeen[$dir]}" ]; then return; fi
+    octavePathsSeen[$dir]=1
+    # addToSearchPath is defined in stdenv/generic/setup.sh. It has the effect
+    # of calling `export X=$dir/...:$X`.
+    addToSearchPath program_PATH $dir/bin
+
+    # Inspect the propagated inputs (if they exist) and recur on them.
+    local prop="$dir/nix-support/propagated-build-inputs"
+    if [ -e $prop ]; then
+    for new_path in $(cat $prop); do
+        _addToOctavePath $new_path
+    done
+    fi
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 761a5faaecf27..c50b6cdf0636e 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -11346,6 +11346,8 @@ in
     overridePlatforms = ["x86_64-linux" "x86_64-darwin"];
   };
 
+  octavePackages = recurseIntoAttrs octave.pkgs;
+
   ocropus = callPackage ../applications/misc/ocropus { };
 
   pachyderm = callPackage ../applications/networking/cluster/pachyderm { };
diff --git a/pkgs/top-level/octave-packages.nix b/pkgs/top-level/octave-packages.nix
new file mode 100644
index 0000000000000..ef5d83b67919f
--- /dev/null
+++ b/pkgs/top-level/octave-packages.nix
@@ -0,0 +1,115 @@
+# This file contains the GNU Octave add-on packages set.
+# Each attribute is an Octave library.
+# Expressions for the Octave libraries are supposed to be in `pkgs/development/octave-modules/<name>/default.nix`.
+
+# When contributing a new package, if that package has a dependency on another
+# octave package, then you DO NOT need to explicitly list it as such when
+# performing the callPackage. It will be passed implicitly.
+# In addition, try to use the same dependencies as the ones octave needs, which
+# should ensure greater compatibility between Octave itself and its packages.
+
+# Like python-packages.nix, packages from top-level.nix are not in the scope
+# of the `callPackage` used for packages here. So, when we do need packages
+# from outside, we can `inherit` them from `pkgs`.
+{ pkgs
+, lib
+, stdenv
+, fetchurl
+, newScope
+, octave
+}:
+
+with lib;
+
+makeScope newScope (self:
+  let
+    inherit (octave) blas lapack gfortran python texinfo gnuplot;
+
+    callPackage = self.callPackage;
+
+    buildOctavePackage = callPackage ../development/interpreters/octave/build-octave-package.nix {
+      inherit lib stdenv;
+      inherit octave;
+      inherit computeRequiredOctavePackages;
+    };
+
+    wrapOctave = callPackage ../development/interpreters/octave/wrap-octave.nix {
+      inherit octave;
+      inherit (pkgs) makeSetupHook makeWrapper;
+    };
+
+    # Given a list of required Octave package derivations, get a list of
+    # ALL required Octave packages needed for the ones specified to run.
+    computeRequiredOctavePackages = drvs: let
+      # Check whether a derivation is an octave package
+      hasOctavePackage = drv: drv?isOctavePackage;
+      packages = filter hasOctavePackage drvs;
+    in unique (packages ++ concatLists (catAttrs "requiredOctavePackages" packages));
+
+  in {
+
+    inherit callPackage buildOctavePackage computeRequiredOctavePackages;
+
+    inherit (callPackage ../development/interpreters/octave/hooks { })
+      writeRequiredOctavePackagesHook;
+
+    arduino = callPackage ../development/octave-modules/arduino {
+      inherit (pkgs) arduino;
+      # Full arduino right now. Might be able to use pkgs.arduino-core
+      # Needs arduinoIDE as a runtime dependency.
+    };
+
+    audio = callPackage ../development/octave-modules/audio {
+      rtmidi = pkgs.rtmidi;
+    };
+
+    bim = callPackage ../development/octave-modules/bim { };
+
+    bsltl = callPackage ../development/octave-modules/bsltl { };
+
+    cgi = callPackage ../development/octave-modules/cgi { };
+
+    communications = callPackage ../development/octave-modules/communications {
+      hdf5 = pkgs.hdf5;
+    };
+
+    control = callPackage ../development/octave-modules/control { };
+
+    io = callPackage ../development/octave-modules/io {
+      unzip = pkgs.unzip;
+    };
+
+    level-set = callPackage ../development/octave-modules/level-set { };
+
+    linear-algebra = callPackage ../development/octave-modules/linear-algebra { };
+
+    ltfat = callPackage ../development/octave-modules/ltfat {
+      fftw = pkgs.fftw;
+      fftwSinglePrec = pkgs.fftwSinglePrec;
+      fftwFloat = pkgs.fftwFloat;
+      fftwLongDouble = pkgs.fftwLongDouble;
+      portaudio = pkgs.portaudio;
+      jre = pkgs.jre;
+    };
+
+    signal = callPackage ../development/octave-modules/signal { };
+
+    symbolic = callPackage ../development/octave-modules/symbolic {
+      # Need to use sympy 1.5.1 for https://github.com/cbm755/octsympy/issues/1023
+      # It has been addressed, but not merged yet.
+      pythonEnv = (let
+        overridenPython = let
+          packageOverrides = self: super: {
+            sympy = super.sympy.overridePythonAttrs (old: rec {
+              version = pkgs.python2Packages.sympy.version;
+              src = pkgs.python2Packages.sympy.src;
+            });
+          };
+        in python.override {inherit packageOverrides; self = overridenPython; };
+      in overridenPython.withPackages (ps: [
+        ps.sympy
+        ps.mpmath
+      ]));
+    };
+
+  })