aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShea Levy <shea@shealevy.com>2018-02-08 11:26:18 -0500
committerShea Levy <shea@shealevy.com>2018-02-08 12:44:37 -0500
commit88cd2d41acb994684a3e4ead1b1676019f43b4b6 (patch)
tree7385aefa5755fd27983f3260b69020acef6de170
parentf201b7733e22cc236a41093a88cc789239d994bd (diff)
Add plugins to make Nix more extensible.
All plugins in plugin-files will be dlopened, allowing them to statically construct instances of the various Register* types Nix supports.
-rw-r--r--.gitignore1
-rw-r--r--Makefile3
-rw-r--r--doc/manual/command-ref/conf-file.xml27
-rw-r--r--doc/manual/release-notes/rl-2.0.xml7
-rw-r--r--mk/libraries.mk7
-rw-r--r--src/build-remote/build-remote.cc2
-rw-r--r--src/libmain/shared.hh1
-rw-r--r--src/libstore/globals.cc15
-rw-r--r--src/libstore/globals.hh7
-rw-r--r--src/libstore/local.mk3
-rwxr-xr-xsrc/nix-build/nix-build.cc2
-rwxr-xr-xsrc/nix-channel/nix-channel.cc3
-rw-r--r--src/nix-collect-garbage/nix-collect-garbage.cc2
-rwxr-xr-xsrc/nix-copy-closure/nix-copy-closure.cc2
-rw-r--r--src/nix-daemon/nix-daemon.cc2
-rw-r--r--src/nix-env/nix-env.cc2
-rw-r--r--src/nix-instantiate/nix-instantiate.cc2
-rw-r--r--src/nix-prefetch-url/nix-prefetch-url.cc2
-rw-r--r--src/nix-store/nix-store.cc2
-rw-r--r--src/nix/main.cc2
-rw-r--r--tests/local.mk5
-rw-r--r--tests/plugins.sh7
-rw-r--r--tests/plugins/local.mk9
-rw-r--r--tests/plugins/plugintest.cc10
24 files changed, 122 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index ce22fa007..0a9599378 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,7 @@ perl/Makefile.config
/scripts/nix-copy-closure
/scripts/nix-reduce-build
/scripts/nix-http-export.cgi
+/scripts/nix-profile-daemon.sh
# /src/libexpr/
/src/libexpr/lexer-tab.cc
diff --git a/Makefile b/Makefile
index 5d8e990cc..c867823fc 100644
--- a/Makefile
+++ b/Makefile
@@ -24,7 +24,8 @@ makefiles = \
misc/launchd/local.mk \
misc/upstart/local.mk \
doc/manual/local.mk \
- tests/local.mk
+ tests/local.mk \
+ tests/plugins/local.mk
GLOBAL_CXXFLAGS += -std=c++14 -g -Wall -include config.h
diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml
index fff7994f2..cede6db3c 100644
--- a/doc/manual/command-ref/conf-file.xml
+++ b/doc/manual/command-ref/conf-file.xml
@@ -742,6 +742,33 @@ builtins.fetchurl {
</varlistentry>
+ <varlistentry xml:id="conf-plugin-files">
+ <term><literal>plugin-files</literal></term>
+ <listitem>
+ <para>
+ A list of plugin files to be loaded by Nix. Each of these
+ files will be dlopened by Nix, allowing them to affect
+ execution through static initialization. In particular, these
+ plugins may construct static instances of RegisterPrimOp to
+ add new primops to the expression language,
+ RegisterStoreImplementation to add new store implementations,
+ and RegisterCommand to add new subcommands to the
+ <literal>nix</literal> command. See the constructors for those
+ types for more details.
+ </para>
+ <para>
+ Since these files are loaded into the same address space as
+ Nix itself, they must be DSOs compatible with the instance of
+ Nix running at the time (i.e. compiled against the same
+ headers, not linked to any incompatible libraries). They
+ should not be linked to any Nix libs directly, as those will
+ be available already at load time.
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+
</variablelist>
</para>
diff --git a/doc/manual/release-notes/rl-2.0.xml b/doc/manual/release-notes/rl-2.0.xml
index 32cdb1d0c..effd2e39d 100644
--- a/doc/manual/release-notes/rl-2.0.xml
+++ b/doc/manual/release-notes/rl-2.0.xml
@@ -389,6 +389,13 @@ configureFlags = "--prefix=${placeholder "out"} --includedir=${placeholder "dev"
</para>
</listitem>
+ <listitem>
+ <para>
+ Nix can now be extended with plugins. See the documentation of
+ the 'plugin-files' option for more details.
+ </para>
+ </listitem>
+
</itemizedlist>
<para>Some features were removed:</para>
diff --git a/mk/libraries.mk b/mk/libraries.mk
index 3cd7a5310..14c95fa91 100644
--- a/mk/libraries.mk
+++ b/mk/libraries.mk
@@ -45,6 +45,11 @@ endif
# - $(1)_INSTALL_DIR: the directory where the library will be
# installed. Defaults to $(libdir).
#
+# - $(1)_EXCLUDE_FROM_LIBRARY_LIST: if defined, the library will not
+# be automatically marked as a dependency of the top-level all
+# target andwill not be listed in the make help output. This is
+# useful for libraries built solely for testing, for example.
+#
# - BUILD_SHARED_LIBS: if equal to ‘1’, a dynamic library will be
# built, otherwise a static library.
define build-library
@@ -149,7 +154,9 @@ define build-library
$(1)_DEPS := $$(foreach fn, $$($(1)_OBJS), $$(call filename-to-dep, $$(fn)))
-include $$($(1)_DEPS)
+ ifndef $(1)_EXCLUDE_FROM_LIBRARY_LIST
libs-list += $$($(1)_PATH)
+ endif
clean-files += $$(_d)/*.a $$(_d)/*.$(SO_EXT) $$(_d)/*.o $$(_d)/.*.dep $$($(1)_DEPS) $$($(1)_OBJS)
dist-files += $$(_srcs)
endef
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc
index df579729a..50eb6b29e 100644
--- a/src/build-remote/build-remote.cc
+++ b/src/build-remote/build-remote.cc
@@ -64,6 +64,8 @@ int main (int argc, char * * argv)
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
+ initPlugins();
+
auto store = openStore().cast<LocalStore>();
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index 1dcc4f0ac..8e4861232 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -22,6 +22,7 @@ public:
int handleExceptions(const string & programName, std::function<void()> fun);
+/* Don't forget to call initPlugins() after settings are initialized! */
void initNix();
void parseCmdLine(int argc, char * * argv,
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index d3c96ddd6..21ab0e629 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -6,6 +6,7 @@
#include <algorithm>
#include <map>
#include <thread>
+#include <dlfcn.h>
namespace nix {
@@ -137,4 +138,18 @@ void MaxBuildJobsSetting::set(const std::string & str)
throw UsageError("configuration setting '%s' should be 'auto' or an integer", name);
}
+
+void initPlugins()
+{
+ for (const auto & pluginFile : settings.pluginFiles.get()) {
+ /* handle is purposefully leaked as there may be state in the
+ DSO needed by the action of the plugin. */
+ void *handle =
+ dlopen(pluginFile.c_str(), RTLD_LAZY | RTLD_LOCAL);
+ if (!handle)
+ throw Error(format("could not dynamically open plugin file '%1%': %2%") % pluginFile % dlerror());
+ }
+}
+
+
}
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 20ac8fe4e..508084d08 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -367,12 +367,19 @@ public:
Setting<Strings> allowedUris{this, {}, "allowed-uris",
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};
+
+ Setting<Paths> pluginFiles{this, {}, "plugin-files",
+ "Plugins to dynamically load at nix initialization time."};
};
// FIXME: don't use a global variable.
extern Settings settings;
+/* This should be called after settings are initialized, but before
+ anything else */
+void initPlugins();
+
extern const string nixVersion;
diff --git a/src/libstore/local.mk b/src/libstore/local.mk
index 50c46ce6f..239356aee 100644
--- a/src/libstore/local.mk
+++ b/src/libstore/local.mk
@@ -9,6 +9,9 @@ libstore_SOURCES := $(wildcard $(d)/*.cc)
libstore_LIBS = libutil libformat
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
+ifneq ($(OS), FreeBSD)
+ libstore_LDFLAGS += -ldl
+endif
libstore_FILES = sandbox-defaults.sb sandbox-minimal.sb sandbox-network.sb
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc
index 1581c282c..99f773451 100755
--- a/src/nix-build/nix-build.cc
+++ b/src/nix-build/nix-build.cc
@@ -232,6 +232,8 @@ void mainWrapped(int argc, char * * argv)
myArgs.parseCmdline(args);
+ initPlugins();
+
if (packages && fromArgs)
throw UsageError("'-p' and '-E' are mutually exclusive");
diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc
index 370f216ab..ec9a7174e 100755
--- a/src/nix-channel/nix-channel.cc
+++ b/src/nix-channel/nix-channel.cc
@@ -213,6 +213,9 @@ int main(int argc, char ** argv)
}
return true;
});
+
+ initPlugins();
+
switch (cmd) {
case cNone:
throw UsageError("no command specified");
diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc
index cc663a969..37fe22f48 100644
--- a/src/nix-collect-garbage/nix-collect-garbage.cc
+++ b/src/nix-collect-garbage/nix-collect-garbage.cc
@@ -77,6 +77,8 @@ int main(int argc, char * * argv)
return true;
});
+ initPlugins();
+
auto profilesDir = settings.nixStateDir + "/profiles";
if (removeOld) removeOldGenerations(profilesDir);
diff --git a/src/nix-copy-closure/nix-copy-closure.cc b/src/nix-copy-closure/nix-copy-closure.cc
index 861fc2e5c..dfb1b8fc5 100755
--- a/src/nix-copy-closure/nix-copy-closure.cc
+++ b/src/nix-copy-closure/nix-copy-closure.cc
@@ -44,6 +44,8 @@ int main(int argc, char ** argv)
return true;
});
+ initPlugins();
+
if (sshHost.empty())
throw UsageError("no host name specified");
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index d3a8ebbdd..890bffa19 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -1060,6 +1060,8 @@ int main(int argc, char * * argv)
return true;
});
+ initPlugins();
+
if (stdio) {
if (getStoreType() == tDaemon) {
/* Forward on this connection to the real daemon */
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 016caf6d2..97e66cbd9 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -1393,6 +1393,8 @@ int main(int argc, char * * argv)
myArgs.parseCmdline(argvToStrings(argc, argv));
+ initPlugins();
+
if (!op) throw UsageError("no operation specified");
auto store = openStore();
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index e05040a42..dd262bea0 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -151,6 +151,8 @@ int main(int argc, char * * argv)
myArgs.parseCmdline(argvToStrings(argc, argv));
+ initPlugins();
+
if (evalOnly && !wantsReadWrite)
settings.readOnlyMode = true;
diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc
index fef3eaa45..fa7ee2545 100644
--- a/src/nix-prefetch-url/nix-prefetch-url.cc
+++ b/src/nix-prefetch-url/nix-prefetch-url.cc
@@ -89,6 +89,8 @@ int main(int argc, char * * argv)
myArgs.parseCmdline(argvToStrings(argc, argv));
+ initPlugins();
+
if (args.size() > 2)
throw UsageError("too many arguments");
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 4fc3421c0..efef7f15c 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -1052,6 +1052,8 @@ int main(int argc, char * * argv)
return true;
});
+ initPlugins();
+
if (!op) throw UsageError("no operation specified");
if (op != opDump && op != opRestore) /* !!! hack */
diff --git a/src/nix/main.cc b/src/nix/main.cc
index 8f6bbe8f5..bb107ec7d 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -92,6 +92,8 @@ void mainWrapped(int argc, char * * argv)
args.parseCmdline(argvToStrings(argc, argv));
+ initPlugins();
+
if (!args.command) args.showHelpAndExit();
Finally f([]() { stopProgressBar(); });
diff --git a/tests/local.mk b/tests/local.mk
index e90b9f7da..51bc09dd4 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -22,7 +22,8 @@ nix_tests = \
run.sh \
brotli.sh \
pure-eval.sh \
- check.sh
+ check.sh \
+ plugins.sh
# parallel.sh
install-tests += $(foreach x, $(nix_tests), tests/$(x))
@@ -31,4 +32,4 @@ tests-environment = NIX_REMOTE= $(bash) -e
clean-files += $(d)/common.sh
-installcheck: $(d)/common.sh
+installcheck: $(d)/common.sh $(d)/plugins/plugintest.so
diff --git a/tests/plugins.sh b/tests/plugins.sh
new file mode 100644
index 000000000..6d18d1da0
--- /dev/null
+++ b/tests/plugins.sh
@@ -0,0 +1,7 @@
+source common.sh
+
+set -o pipefail
+
+res=$(nix eval '(builtins.constNull true)' --option plugin-files $PWD/plugins/plugintest.so)
+
+[ "$res"x = "nullx" ]
diff --git a/tests/plugins/local.mk b/tests/plugins/local.mk
new file mode 100644
index 000000000..a5f19b087
--- /dev/null
+++ b/tests/plugins/local.mk
@@ -0,0 +1,9 @@
+libraries += plugintest
+
+plugintest_DIR := $(d)
+
+plugintest_SOURCES := $(d)/plugintest.cc
+
+plugintest_ALLOW_UNDEFINED := 1
+
+plugintest_EXCLUDE_FROM_LIBRARY_LIST := 1
diff --git a/tests/plugins/plugintest.cc b/tests/plugins/plugintest.cc
new file mode 100644
index 000000000..f788c4814
--- /dev/null
+++ b/tests/plugins/plugintest.cc
@@ -0,0 +1,10 @@
+#include "primops.hh"
+
+using namespace nix;
+
+static void prim_constNull (EvalState & state, const Pos & pos, Value ** args, Value & v)
+{
+ mkNull(v);
+}
+
+static RegisterPrimOp r("constNull", 1, prim_constNull);