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.
This commit is contained in:
parent
f201b7733e
commit
88cd2d41ac
24 changed files with 122 additions and 3 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -38,6 +38,7 @@ perl/Makefile.config
|
||||||
/scripts/nix-copy-closure
|
/scripts/nix-copy-closure
|
||||||
/scripts/nix-reduce-build
|
/scripts/nix-reduce-build
|
||||||
/scripts/nix-http-export.cgi
|
/scripts/nix-http-export.cgi
|
||||||
|
/scripts/nix-profile-daemon.sh
|
||||||
|
|
||||||
# /src/libexpr/
|
# /src/libexpr/
|
||||||
/src/libexpr/lexer-tab.cc
|
/src/libexpr/lexer-tab.cc
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -24,7 +24,8 @@ makefiles = \
|
||||||
misc/launchd/local.mk \
|
misc/launchd/local.mk \
|
||||||
misc/upstart/local.mk \
|
misc/upstart/local.mk \
|
||||||
doc/manual/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
|
GLOBAL_CXXFLAGS += -std=c++14 -g -Wall -include config.h
|
||||||
|
|
||||||
|
|
|
@ -742,6 +742,33 @@ builtins.fetchurl {
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
|
|
|
@ -389,6 +389,13 @@ configureFlags = "--prefix=${placeholder "out"} --includedir=${placeholder "dev"
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Nix can now be extended with plugins. See the documentation of
|
||||||
|
the 'plugin-files' option for more details.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
<para>Some features were removed:</para>
|
<para>Some features were removed:</para>
|
||||||
|
|
|
@ -45,6 +45,11 @@ endif
|
||||||
# - $(1)_INSTALL_DIR: the directory where the library will be
|
# - $(1)_INSTALL_DIR: the directory where the library will be
|
||||||
# installed. Defaults to $(libdir).
|
# 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
|
# - BUILD_SHARED_LIBS: if equal to ‘1’, a dynamic library will be
|
||||||
# built, otherwise a static library.
|
# built, otherwise a static library.
|
||||||
define build-library
|
define build-library
|
||||||
|
@ -149,7 +154,9 @@ define build-library
|
||||||
$(1)_DEPS := $$(foreach fn, $$($(1)_OBJS), $$(call filename-to-dep, $$(fn)))
|
$(1)_DEPS := $$(foreach fn, $$($(1)_OBJS), $$(call filename-to-dep, $$(fn)))
|
||||||
-include $$($(1)_DEPS)
|
-include $$($(1)_DEPS)
|
||||||
|
|
||||||
|
ifndef $(1)_EXCLUDE_FROM_LIBRARY_LIST
|
||||||
libs-list += $$($(1)_PATH)
|
libs-list += $$($(1)_PATH)
|
||||||
|
endif
|
||||||
clean-files += $$(_d)/*.a $$(_d)/*.$(SO_EXT) $$(_d)/*.o $$(_d)/.*.dep $$($(1)_DEPS) $$($(1)_OBJS)
|
clean-files += $$(_d)/*.a $$(_d)/*.$(SO_EXT) $$(_d)/*.o $$(_d)/.*.dep $$($(1)_DEPS) $$($(1)_OBJS)
|
||||||
dist-files += $$(_srcs)
|
dist-files += $$(_srcs)
|
||||||
endef
|
endef
|
||||||
|
|
|
@ -64,6 +64,8 @@ int main (int argc, char * * argv)
|
||||||
|
|
||||||
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
|
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
auto store = openStore().cast<LocalStore>();
|
auto store = openStore().cast<LocalStore>();
|
||||||
|
|
||||||
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since
|
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
|
|
||||||
int handleExceptions(const string & programName, std::function<void()> fun);
|
int handleExceptions(const string & programName, std::function<void()> fun);
|
||||||
|
|
||||||
|
/* Don't forget to call initPlugins() after settings are initialized! */
|
||||||
void initNix();
|
void initNix();
|
||||||
|
|
||||||
void parseCmdLine(int argc, char * * argv,
|
void parseCmdLine(int argc, char * * argv,
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
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);
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,12 +367,19 @@ public:
|
||||||
|
|
||||||
Setting<Strings> allowedUris{this, {}, "allowed-uris",
|
Setting<Strings> allowedUris{this, {}, "allowed-uris",
|
||||||
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};
|
"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.
|
// FIXME: don't use a global variable.
|
||||||
extern Settings settings;
|
extern Settings settings;
|
||||||
|
|
||||||
|
/* This should be called after settings are initialized, but before
|
||||||
|
anything else */
|
||||||
|
void initPlugins();
|
||||||
|
|
||||||
|
|
||||||
extern const string nixVersion;
|
extern const string nixVersion;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,9 @@ libstore_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
libstore_LIBS = libutil libformat
|
libstore_LIBS = libutil libformat
|
||||||
|
|
||||||
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
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
|
libstore_FILES = sandbox-defaults.sb sandbox-minimal.sb sandbox-network.sb
|
||||||
|
|
||||||
|
|
|
@ -232,6 +232,8 @@ void mainWrapped(int argc, char * * argv)
|
||||||
|
|
||||||
myArgs.parseCmdline(args);
|
myArgs.parseCmdline(args);
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (packages && fromArgs)
|
if (packages && fromArgs)
|
||||||
throw UsageError("'-p' and '-E' are mutually exclusive");
|
throw UsageError("'-p' and '-E' are mutually exclusive");
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,9 @@ int main(int argc, char ** argv)
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case cNone:
|
case cNone:
|
||||||
throw UsageError("no command specified");
|
throw UsageError("no command specified");
|
||||||
|
|
|
@ -77,6 +77,8 @@ int main(int argc, char * * argv)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
auto profilesDir = settings.nixStateDir + "/profiles";
|
auto profilesDir = settings.nixStateDir + "/profiles";
|
||||||
if (removeOld) removeOldGenerations(profilesDir);
|
if (removeOld) removeOldGenerations(profilesDir);
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ int main(int argc, char ** argv)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (sshHost.empty())
|
if (sshHost.empty())
|
||||||
throw UsageError("no host name specified");
|
throw UsageError("no host name specified");
|
||||||
|
|
||||||
|
|
|
@ -1060,6 +1060,8 @@ int main(int argc, char * * argv)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (stdio) {
|
if (stdio) {
|
||||||
if (getStoreType() == tDaemon) {
|
if (getStoreType() == tDaemon) {
|
||||||
/* Forward on this connection to the real daemon */
|
/* Forward on this connection to the real daemon */
|
||||||
|
|
|
@ -1393,6 +1393,8 @@ int main(int argc, char * * argv)
|
||||||
|
|
||||||
myArgs.parseCmdline(argvToStrings(argc, argv));
|
myArgs.parseCmdline(argvToStrings(argc, argv));
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (!op) throw UsageError("no operation specified");
|
if (!op) throw UsageError("no operation specified");
|
||||||
|
|
||||||
auto store = openStore();
|
auto store = openStore();
|
||||||
|
|
|
@ -151,6 +151,8 @@ int main(int argc, char * * argv)
|
||||||
|
|
||||||
myArgs.parseCmdline(argvToStrings(argc, argv));
|
myArgs.parseCmdline(argvToStrings(argc, argv));
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (evalOnly && !wantsReadWrite)
|
if (evalOnly && !wantsReadWrite)
|
||||||
settings.readOnlyMode = true;
|
settings.readOnlyMode = true;
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,8 @@ int main(int argc, char * * argv)
|
||||||
|
|
||||||
myArgs.parseCmdline(argvToStrings(argc, argv));
|
myArgs.parseCmdline(argvToStrings(argc, argv));
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (args.size() > 2)
|
if (args.size() > 2)
|
||||||
throw UsageError("too many arguments");
|
throw UsageError("too many arguments");
|
||||||
|
|
||||||
|
|
|
@ -1052,6 +1052,8 @@ int main(int argc, char * * argv)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (!op) throw UsageError("no operation specified");
|
if (!op) throw UsageError("no operation specified");
|
||||||
|
|
||||||
if (op != opDump && op != opRestore) /* !!! hack */
|
if (op != opDump && op != opRestore) /* !!! hack */
|
||||||
|
|
|
@ -92,6 +92,8 @@ void mainWrapped(int argc, char * * argv)
|
||||||
|
|
||||||
args.parseCmdline(argvToStrings(argc, argv));
|
args.parseCmdline(argvToStrings(argc, argv));
|
||||||
|
|
||||||
|
initPlugins();
|
||||||
|
|
||||||
if (!args.command) args.showHelpAndExit();
|
if (!args.command) args.showHelpAndExit();
|
||||||
|
|
||||||
Finally f([]() { stopProgressBar(); });
|
Finally f([]() { stopProgressBar(); });
|
||||||
|
|
|
@ -22,7 +22,8 @@ nix_tests = \
|
||||||
run.sh \
|
run.sh \
|
||||||
brotli.sh \
|
brotli.sh \
|
||||||
pure-eval.sh \
|
pure-eval.sh \
|
||||||
check.sh
|
check.sh \
|
||||||
|
plugins.sh
|
||||||
# parallel.sh
|
# parallel.sh
|
||||||
|
|
||||||
install-tests += $(foreach x, $(nix_tests), tests/$(x))
|
install-tests += $(foreach x, $(nix_tests), tests/$(x))
|
||||||
|
@ -31,4 +32,4 @@ tests-environment = NIX_REMOTE= $(bash) -e
|
||||||
|
|
||||||
clean-files += $(d)/common.sh
|
clean-files += $(d)/common.sh
|
||||||
|
|
||||||
installcheck: $(d)/common.sh
|
installcheck: $(d)/common.sh $(d)/plugins/plugintest.so
|
||||||
|
|
7
tests/plugins.sh
Normal file
7
tests/plugins.sh
Normal file
|
@ -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" ]
|
9
tests/plugins/local.mk
Normal file
9
tests/plugins/local.mk
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
libraries += plugintest
|
||||||
|
|
||||||
|
plugintest_DIR := $(d)
|
||||||
|
|
||||||
|
plugintest_SOURCES := $(d)/plugintest.cc
|
||||||
|
|
||||||
|
plugintest_ALLOW_UNDEFINED := 1
|
||||||
|
|
||||||
|
plugintest_EXCLUDE_FROM_LIBRARY_LIST := 1
|
10
tests/plugins/plugintest.cc
Normal file
10
tests/plugins/plugintest.cc
Normal file
|
@ -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);
|
Loading…
Reference in a new issue