Nix can be used to build any kind of package. But here I'm just going to focus on the simple C&C++ case.
Firstly we have to know that the final built packages will located inside /nix/store
. Which is globally readable directory of all build inputs and build outputs of the Nix system. The emphasis is on readable, not writable, that is /nix/store
is meant to be modified by the user or programs except for the Nix system utilities. This centralises the management of packages, and keeps our packages and package configuration consistent.
So what exactly are we trying to build. Our goal is to build a directory that will be located in /nix/store/*-package-version/
, where *
is the hash of the package. Preferably a version
is also available, but some C&C++ packages don't have versions, so in that case, there's only /nix/store/*-package/
.
What will be inside this directory? It follows the GNU Coding Standards described here: https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
Here's an imaginary package (with the /nix/store/
omitted):
*-package-version/bin/ # executables
*-package-version/lib/ # shared libraries
*-package-version/include/ # header files
*-package-version/etc/ # configuration files
*-package-version/share/ # architecture independent data files
*-package-version/share/man # man pages
*-package-version/share/man/man1 # man for general commands
*-package-version/share/man/man6 # man for games and screensavers
*-package-version/share/man/man8 # man for system administration commands and daemons
*-package-version/share/info # info pages
*-package-version/share/doc/package-version/ # arbitrary documentation
*-package-version/share/doc/package-version/pdf/ # pdf documentation
*-package-version/share/doc/package-version/ps/ # ps docuumentation
*-package-version/share/doc/package-version/html/ # html documentation
*-package-version/share/doc/package-version/html/en/ # language specific html documentation
Notice we don't really have /var
or /run
or /com
inside the /nix/store
, any programs requiring these, will be patched or configured to use locations outside of /nix/store
.
When such a package will is installed, the relevant directories and files will be symlinked to either ~/.nix-profile/
, or in /run/current-system/sw/
, allowing you access the man pages, the shared libraries, their configuration and other documentation.
The package we are going to try to build is the TraceFileGen and TraceFileSim from the GarCoSim project.
For these 2 packages, we shall create them in nixpkgs/pkgs/development/tools/analysis/garcosim/tracefilegen
and nixpkgs/pkgs/development/tools/analysis/garcosim/tracefilesim
. We will also modify the nixpkgs/pkgs/top-level/all-packages.nix
to add the top-level aliases for the 2 packages that we are creating.
The key tools that help us create these 2 packages are:
nix-repl
from thenix-repl
packagenix-build
nix-prefetch-git
from thenix-prefetch-scripts
package
Before we even get to developing our package build and deployment scripts, we need to first content address our dependencies. We'll use nix-prefetch-git
for this, as the 2 packages are exported via github.com. Remember to use a revision SHA256 so as to make our package deterministic. Even if you want a release tag, it's still better to get the content address associated to the release tag, as the tag is mutable (can be changed willy-nilly by the package author), but the content address isn't. We can however use a corresponding release tag as a mutable semantic label, designed for human readable aliases.
> nix-prefetch-git --url "https://github.com/GarCoSim/TraceFileGen.git" --rev "4acf75b142683cc475c6b1c841a221db0753b404"
Initialized empty Git repository in /tmp/git-checkout-tmp-rmmqIKdj/git-export/.git/
remote: Counting objects: 161, done.
remote: Compressing objects: 100% (111/111), done.
remote: Total 161 (delta 73), reused 111 (delta 46), pack-reused 0
Receiving objects: 100% (161/161), 163.40 KiB | 101.00 KiB/s, done.
Resolving deltas: 100% (73/73), done.
From https://github.com/GarCoSim/TraceFileGen
* branch HEAD -> FETCH_HEAD
Switched to a new branch 'fetchgit'
git revision is 4acf75b142683cc475c6b1c841a221db0753b404
git human-readable version is -- none --
Commit date is 2015-11-13 11:13:13 -0400
removing `.git'...
hash is 69b056298cf570debd3718b2e2cb7e63ad9465919c8190cf38043791ce61d0d6
path is /nix/store/wda0pgx1m01aza4d1mhh35yp6di97bcl-git-export
69b056298cf570debd3718b2e2cb7e63ad9465919c8190cf38043791ce61d0d6
Now we know the package hash that we shall use for content addressing the upstream package. Repeat for TraceFileSim.
We can now develop the package build and deploy scripts.
For TraceFileGen, we actually have 2 files, this is because its build phase is non-standard.
Here's our default.nix
a Nix expression that will be deriving the package:
{ stdenv, fetchgit, cmake }:
stdenv.mkDerivation rec {
name = "tracefilegen";
src = fetchgit {
url = "https://github.com/GarCoSim/TraceFileGen.git";
rev = "4acf75b142683cc475c6b1c841a221db0753b404";
sha256 = "69b056298cf570debd3718b2e2cb7e63ad9465919c8190cf38043791ce61d0d6";
};
buildInputs = [ cmake ];
builder = ./builder.sh;
meta = with stdenv.lib; {
description = "Automatically generate all types of basic memory management operations and write into trace files";
homepage = "https://github.com/GarCoSim";
maintainers = [ maintainers.cmcdragonkai ];
license = licenses.gpl2;
platforms = platforms.linux;
};
}
And here's the builder.sh
:
source "$stdenv"/setup
cp --recursive "$src" ./
chmod --recursive u=rwx ./"$(basename "$src")"
cd ./"$(basename "$src")"
cmake ./
make
mkdir --parents "$out"/bin
cp ./TraceFileGen "$out"/bin
mkdir --parents "$out"/share/doc/"$name"/html
cp --recursive ./Documentation/html/* "$out/share/doc/$name/html/"
While for TraceFileSim, it's very standardised build phase, but its install phase is not standardised, so we just have to override the installPhase
:
{ stdenv, fetchgit }:
stdenv.mkDerivation {
name = "tracefilesim";
src = fetchgit {
url = "https://github.com/GarCoSim/TraceFileSim.git";
rev = "368aa6b1d6560e7ecbd16fca47000c8f528f3da2";
sha256 = "22dfb60d1680ce6c98d60d12c0d0950073f02359605fcdef625e3049bca07809";
};
installPhase = ''
mkdir --parents "$out/bin"
cp ./traceFileSim "$out/bin"
'';
meta = with stdenv.lib; {
description = "Ease the analysis of existing memory management techniques, as well as the prototyping of new memory management techniques.";
homepage = "https://github.com/GarCoSim";
maintainers = [ maintainers.cmcdragonkai ];
licenses = licenses.gpl2;
platforms = platforms.linux;
};
}
Before we modify nixpkgs/pkgs/top-level/all-packages.nix
, we need to test if our package works locally, this can easily be done using nix-build
.
nix-build --keep-failed --expr 'with import <nixpkgs> {}; callPackage ./default.nix {}'
When it fails, because it will probably do so before you get your build scripts correct, you can find the build directory inside /tmp
. The state of the directory will be left at exactly when the build was considered to be failed. This can help you iteratively build the build and deploy scripts.
Repeat for both packages.
When a build succeeds, it will place a ./result
symlink that goes to the built package in /nix/store
. Here you can use it to test the binaries and documentation. It will not be "installed" on your system, so once you remove the ./result
symlink, the build artifacts can be garbage collected by Nix.
Once that is done, we can now modify the all-packages.nix
to include:
...
tracefilegen = callPackage ../development/tools/analysis/garcosim/tracefilegen { };
tracefilesim = callPackage ../development/tools/analysis/garcosim/tracefilesim { };
...
It appears that there isn't much of an order to all-packages.nix
, so I just placed it near other packages that had trace
in their name.
Note that it is possible to imperatively compile something. Just do it at the user level.
nix-env -iA nixos.gcc
nix-env -iA nixos.gnumake
nix-env -iA nixos.cmake
You can now, run make
and cmake
in user space.
Nix's mkDerivation
is quite advanced and can deal with most standardised configure && make && make install
packages. It even detects cmake
packages and appropriately configures them. But this time, the 2 packages did not respect the usual configuration parameters of cmake
that allows one to specify the output directory. Instead a manual copy of the binaries and documentation was required.
Note that the src
attribute can be specified to a local directory. However be aware that ./.
is valid for the surrounding directory, but not ./
nor .
.
Once you have submitted a PR, or are looking at somebody else's PR for a package or change to NixOS or nixpkgs.
It's easy to check whether the commit has been applied to your branches if you're using pinned OS nixpkgs.
You just do git branch --contains <commit-id> 2>/dev/null
. You just need to use the PR commit hash for this.
The command will list all the branches which contain that commit.
Some gnome packages require wrapGAppsHook
to be put into their nativeBuildInputs
especially when dealing with Gnome settings error.
You should be putting pkgconfig
in the nativeBuildInputs
instead of buildInputs
. Cause it's generally only required at build time, and not runtime.
Even statically linked libraries are still considered buildInputs
since the code that gets generated is still running at runtime.
Sometimes deps you need isn't available.
So use things like nix-shell
to find things. Use :l <nixpkgs>
to load global nixpkgs. Use pkgs = import (fetchTarball ....tar.gz) {}
to load other kinds of nixpgks. I think you can also do :l ~/Projects/nixpkgs
to nixpkgs as directories as well.
Finally remember once you have checked out your own branch, you can do nix-env -f ~/Projects/nixpkgs -i gpredict
to install a package that only exists on your dev branch.
shellHook = ''
echo 'Entering ShelfTrend API Environment'
. ./.env
set -v
alias mysql="\mysql --socket='./.mysql/mysql.sock' "$SHELFTREND_DB_DATABASE""
alias mysqladmin="\mysqladmin --socket='./.mysql/mysql.sock'"
alias mysqld="\mysqld \
--datadir="$(pwd)/.mysql" \
--socket="$(pwd)/.mysql/mysql.sock" \
--bind-address="$SHELFTREND_DB_HOST" \
--port="$SHELFTREND_DB_PORT""
alias flyway="\flyway \
-user="$SHELFTREND_DB_USERNAME" \
-password="$SHELFTREND_DB_PASSWORD" \
-url="jdbc:mysql://$SHELFTREND_DB_HOST:$SHELFTREND_DB_PORT/$SHELFTREND_DB_DATABASE" \
-locations=filesystem:$(pwd)/migrations";
set +v
'';