1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-06 21:41:48 +02:00

Merge branch 'master' of github.com:NixOS/nix into path-in-exec

This commit is contained in:
siddhantCodes 2024-08-17 20:05:31 +05:30
commit 58ef129502
99 changed files with 1449 additions and 787 deletions

View file

@ -171,7 +171,9 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
{
if (EvalSettings::isPseudoUrl(s)) {
auto accessor = fetchers::downloadTarball(
EvalSettings::resolvePseudoUrl(s)).accessor;
state.store,
state.fetchSettings,
EvalSettings::resolvePseudoUrl(s));
auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy);
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
}

View file

@ -7,7 +7,6 @@
#include "ansicolor.hh"
#include "shared.hh"
#include "config-global.hh"
#include "eval.hh"
#include "eval-settings.hh"
#include "attr-path.hh"
@ -77,10 +76,14 @@ struct NixRepl
int displ;
StringSet varNames;
RunNix * runNixPtr;
void runNix(Path program, const Strings & args, const std::optional<std::string> & input = {});
std::unique_ptr<ReplInteracter> interacter;
NixRepl(const LookupPath & lookupPath, nix::ref<Store> store,ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
std::function<AnnotatedValues()> getValues, RunNix * runNix);
virtual ~NixRepl() = default;
ReplExitStatus mainLoop() override;
@ -125,32 +128,16 @@ std::string removeWhitespace(std::string s)
NixRepl::NixRepl(const LookupPath & lookupPath, nix::ref<Store> store, ref<EvalState> state,
std::function<NixRepl::AnnotatedValues()> getValues)
std::function<NixRepl::AnnotatedValues()> getValues, RunNix * runNix = nullptr)
: AbstractNixRepl(state)
, debugTraceIndex(0)
, getValues(getValues)
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv.get()))
, runNixPtr{runNix}
, interacter(make_unique<ReadlineLikeInteracter>(getDataDir() + "/nix/repl-history"))
{
}
void runNix(Path program, const Strings & args,
const std::optional<std::string> & input = {})
{
auto subprocessEnv = getEnv();
subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue();
//isInteractive avoid grabling interactive commands
runProgram2(RunOptions {
.program = settings.nixBinDir+ "/" + program,
.args = args,
.environment = subprocessEnv,
.input = input,
.isInteractive = true,
});
return;
}
static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt)
{
if (dt.isError)
@ -833,9 +820,18 @@ void NixRepl::evalString(std::string s, Value & v)
}
void NixRepl::runNix(Path program, const Strings & args, const std::optional<std::string> & input)
{
if (runNixPtr)
(*runNixPtr)(program, args, input);
else
throw Error("Cannot run '%s', no method of calling the Nix CLI provided", program);
}
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const LookupPath & lookupPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues)
std::function<AnnotatedValues()> getValues, RunNix * runNix)
{
return std::make_unique<NixRepl>(
lookupPath,

View file

@ -19,9 +19,19 @@ struct AbstractNixRepl
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
using RunNix = void(Path program, const Strings & args, const std::optional<std::string> & input);
/**
* @param runNix Function to run the nix CLI to support various
* `:<something>` commands. Optional; if not provided,
* everything else will still work fine, but those commands won't.
*/
static std::unique_ptr<AbstractNixRepl> create(
const LookupPath & lookupPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
const LookupPath & lookupPath,
nix::ref<Store> store,
ref<EvalState> state,
std::function<AnnotatedValues()> getValues,
RunNix * runNix = nullptr);
static ReplExitStatus runSimple(
ref<EvalState> evalState,

View file

@ -3088,7 +3088,9 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
if (EvalSettings::isPseudoUrl(value)) {
try {
auto accessor = fetchers::downloadTarball(
EvalSettings::resolvePseudoUrl(value)).accessor;
store,
fetchSettings,
EvalSettings::resolvePseudoUrl(value));
auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy);
return finish(store->toRealPath(storePath));
} catch (Error & e) {

View file

@ -4857,7 +4857,10 @@ void EvalState::createBaseEnv()
addConstant("__nixPath", v, {
.type = nList,
.doc = R"(
The value of the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path): a list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md).
A list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md).
Its value is primarily determined by the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path), which are
- Overridden by the [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH) environment variable or the `--nix-path` option
- Extended by the [`-I` option](@docroot@/command-ref/opt-common.md#opt-I) or `--extra-nix-path`
> **Example**
>

View file

@ -507,7 +507,11 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
// https://github.com/NixOS/nix/issues/4313
auto storePath =
unpack
? fetchToStore(*state.store, fetchers::downloadTarball(*url).accessor, FetchMode::Copy, name)
? fetchToStore(
*state.store,
fetchers::downloadTarball(state.store, state.fetchSettings, *url),
FetchMode::Copy,
name)
: fetchers::downloadFile(state.store, *url, name).storePath;
if (expectedHash) {

View file

@ -331,9 +331,9 @@ public:
void mkStringMove(const char * s, const NixStringContext & context);
inline void mkString(const Symbol & s)
inline void mkString(const SymbolStr & s)
{
mkString(((const std::string &) s).c_str());
mkString(s.c_str());
}
void mkPath(const SourcePath & path);

View file

@ -102,7 +102,7 @@ DownloadFileResult downloadFile(
};
}
DownloadTarballResult downloadTarball(
static DownloadTarballResult downloadTarball_(
const std::string & url,
const Headers & headers)
{
@ -202,6 +202,22 @@ DownloadTarballResult downloadTarball(
return attrsToResult(infoAttrs);
}
ref<SourceAccessor> downloadTarball(
ref<Store> store,
const Settings & settings,
const std::string & url)
{
/* Go through Input::getAccessor() to ensure that the resulting
accessor has a fingerprint. */
fetchers::Attrs attrs;
attrs.insert_or_assign("type", "tarball");
attrs.insert_or_assign("url", url);
auto input = Input::fromAttrs(settings, std::move(attrs));
return input.getAccessor(store).first;
}
// An input scheme corresponding to a curl-downloadable resource.
struct CurlInputScheme : InputScheme
{
@ -353,7 +369,7 @@ struct TarballInputScheme : CurlInputScheme
{
auto input(_input);
auto result = downloadTarball(getStrAttr(input.attrs, "url"), {});
auto result = downloadTarball_(getStrAttr(input.attrs, "url"), {});
result.accessor->setPathDisplay("«" + input.to_string() + "»");

View file

@ -14,6 +14,8 @@ struct SourceAccessor;
namespace nix::fetchers {
struct Settings;
struct DownloadFileResult
{
StorePath storePath;
@ -40,8 +42,9 @@ struct DownloadTarballResult
* Download and import a tarball into the Git cache. The result is the
* Git tree hash of the root directory.
*/
DownloadTarballResult downloadTarball(
const std::string & url,
const Headers & headers = {});
ref<SourceAccessor> downloadTarball(
ref<Store> store,
const Settings & settings,
const std::string & url);
}

View file

@ -64,7 +64,6 @@ Settings::Settings()
, nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
, nixConfDir(canonPath(getEnvNonEmpty("NIX_CONF_DIR").value_or(NIX_CONF_DIR)))
, nixUserConfFiles(getUserConfigFiles())
, nixBinDir(canonPath(getEnvNonEmpty("NIX_BIN_DIR").value_or(NIX_BIN_DIR)))
, nixManDir(canonPath(NIX_MAN_DIR))
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
{
@ -95,34 +94,6 @@ Settings::Settings()
sandboxPaths = tokenizeString<StringSet>("/System/Library/Frameworks /System/Library/PrivateFrameworks /bin/sh /bin/bash /private/tmp /private/var/tmp /usr/lib");
allowedImpureHostPrefixes = tokenizeString<StringSet>("/System/Library /usr/lib /dev /bin/sh");
#endif
/* Set the build hook location
For builds we perform a self-invocation, so Nix has to be self-aware.
That is, it has to know where it is installed. We don't think it's sentient.
Normally, nix is installed according to `nixBinDir`, which is set at compile time,
but can be overridden. This makes for a great default that works even if this
code is linked as a library into some other program whose main is not aware
that it might need to be a build remote hook.
However, it may not have been installed at all. For example, if it's a static build,
there's a good chance that it has been moved out of its installation directory.
That makes `nixBinDir` useless. Instead, we'll query the OS for the path to the
current executable, using `getSelfExe()`.
As a last resort, we resort to `PATH`. Hopefully we find a `nix` there that's compatible.
If you're porting Nix to a new platform, that might be good enough for a while, but
you'll want to improve `getSelfExe()` to work on your platform.
*/
std::string nixExePath = nixBinDir + "/nix";
if (!pathExists(nixExePath)) {
nixExePath = getSelfExe().value_or("nix");
}
buildHook = {
nixExePath,
"__build-remote",
};
}
void loadConfFile(AbstractConfig & config)

View file

@ -84,11 +84,6 @@ public:
*/
std::vector<Path> nixUserConfFiles;
/**
* The directory where the main programs are stored.
*/
Path nixBinDir;
/**
* The directory where the man pages are stored.
*/
@ -246,7 +241,7 @@ public:
)",
{"build-timeout"}};
Setting<Strings> buildHook{this, {}, "build-hook",
Setting<Strings> buildHook{this, {"nix", "__build-remote"}, "build-hook",
R"(
The path to the helper program that executes remote builds.

View file

@ -71,7 +71,6 @@ libstore_CXXFLAGS += \
-DNIX_STATE_DIR=\"$(NIX_ROOT)$(localstatedir)/nix\" \
-DNIX_LOG_DIR=\"$(NIX_ROOT)$(localstatedir)/log/nix\" \
-DNIX_CONF_DIR=\"$(NIX_ROOT)$(sysconfdir)/nix\" \
-DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\" \
-DNIX_MAN_DIR=\"$(NIX_ROOT)$(mandir)\" \
-DLSOF=\"$(NIX_ROOT)$(lsof)\"

View file

@ -21,7 +21,7 @@ configdata = configuration_data()
# TODO rename, because it will conflict with downstream projects
configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
configdata.set_quoted('SYSTEM', host_machine.system())
configdata.set_quoted('SYSTEM', host_machine.cpu_family() + '-' + host_machine.system())
deps_private_maybe_subproject = [
]
@ -328,7 +328,6 @@ prefix = get_option('prefix')
path_opts = [
# Meson built-ins.
'datadir',
'bindir',
'mandir',
'libdir',
'includedir',
@ -373,7 +372,6 @@ cpp_str_defines = {
'NIX_STATE_DIR': state_dir / 'nix',
'NIX_LOG_DIR': log_dir,
'NIX_CONF_DIR': sysconfdir / 'nix',
'NIX_BIN_DIR': bindir,
'NIX_MAN_DIR': mandir,
}

View file

@ -4,6 +4,7 @@
#include "file-system.hh"
#include "child.hh"
#include "strings.hh"
#include "executable-path.hh"
namespace nix {
@ -16,11 +17,18 @@ HookInstance::HookInstance()
if (buildHookArgs.empty())
throw Error("'build-hook' setting is empty");
auto buildHook = canonPath(buildHookArgs.front());
std::filesystem::path buildHook = buildHookArgs.front();
buildHookArgs.pop_front();
try {
buildHook = ExecutablePath::load().findPath(buildHook);
} catch (ExecutableLookupError & e) {
e.addTrace(nullptr, "while resolving the 'build-hook' setting'");
throw;
}
Strings args;
args.push_back(std::string(baseNameOf(buildHook)));
args.push_back(buildHook.filename().string());
for (auto & arg : buildHookArgs)
args.push_back(arg);
@ -59,7 +67,7 @@ HookInstance::HookInstance()
if (dup2(builderOut.readSide.get(), 5) == -1)
throw SysError("dupping builder's stdout/stderr");
execv(buildHook.c_str(), stringsToCharPtrs(args).data());
execv(buildHook.native().c_str(), stringsToCharPtrs(args).data());
throw SysError("executing '%s'", buildHook);
});

View file

@ -49,6 +49,7 @@ R""(
(if (param "_ALLOW_LOCAL_NETWORKING")
(begin
(allow network* (remote ip "localhost:*"))
(allow network-inbound (local ip "*:*")) ; required to bind and listen
; Allow access to /etc/resolv.conf (which is a symlink to
; /private/var/run/resolv.conf).

View file

@ -60,7 +60,7 @@ OsString ExecutablePath::render() const
}
std::optional<fs::path>
ExecutablePath::find(const OsString & exe, std::function<bool(const fs::path &)> isExecutable) const
ExecutablePath::findName(const OsString & exe, std::function<bool(const fs::path &)> isExecutable) const
{
// "If the pathname being sought contains a <slash>, the search
// through the path prefixes shall not be performed."
@ -76,4 +76,20 @@ ExecutablePath::find(const OsString & exe, std::function<bool(const fs::path &)>
return std::nullopt;
}
fs::path ExecutablePath::findPath(const fs::path & exe, std::function<bool(const fs::path &)> isExecutable) const
{
// "If the pathname being sought contains a <slash>, the search
// through the path prefixes shall not be performed."
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
if (exe.filename() == exe) {
auto resOpt = findName(exe, isExecutable);
if (resOpt)
return *resOpt;
else
throw ExecutableLookupError("Could not find executable '%s'", exe.native());
} else {
return exe;
}
}
} // namespace nix

View file

@ -5,6 +5,8 @@
namespace nix {
MakeError(ExecutableLookupError, Error);
struct ExecutablePath
{
std::vector<std::filesystem::path> directories;
@ -54,10 +56,21 @@ struct ExecutablePath
*
* @return path to a resolved executable
*/
std::optional<std::filesystem::path> find(
std::optional<std::filesystem::path> findName(
const OsString & exe,
std::function<bool(const std::filesystem::path &)> isExecutableFile = isExecutableFileAmbient) const;
/**
* Like the `findName` but also allows a file path as input.
*
* This implements the full POSIX spec: if the path is just a name,
* it searches like the above. Otherwise, it returns the path as is.
* If (in the name case) the search fails, an exception is thrown.
*/
std::filesystem::path findPath(
const std::filesystem::path & exe,
std::function<bool(const std::filesystem::path &)> isExecutable = isExecutableFileAmbient) const;
bool operator==(const ExecutablePath &) const = default;
};

View file

@ -7,6 +7,7 @@
#include "eval-settings.hh" // for defexpr
#include "users.hh"
#include "tarball.hh"
#include "self-exe.hh"
#include <fcntl.h>
#include <regex>
@ -67,7 +68,7 @@ static void removeChannel(const std::string & name)
channels.erase(name);
writeChannels();
runProgram(settings.nixBinDir + "/nix-env", true, { "--profile", profile, "--uninstall", name });
runProgram(getNixBin("nix-env").string(), true, { "--profile", profile, "--uninstall", name });
}
static Path nixDefExpr;
@ -118,7 +119,7 @@ static void update(const StringSet & channelNames)
bool unpacked = false;
if (std::regex_search(filename, std::regex("\\.tar\\.(gz|bz2|xz)$"))) {
runProgram(settings.nixBinDir + "/nix-build", false, { "--no-out-link", "--expr", "import " + unpackChannelPath +
runProgram(getNixBin("nix-build").string(), false, { "--no-out-link", "--expr", "import " + unpackChannelPath +
"{ name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; }" });
unpacked = true;
}
@ -143,7 +144,7 @@ static void update(const StringSet & channelNames)
for (auto & expr : exprs)
envArgs.push_back(std::move(expr));
envArgs.push_back("--quiet");
runProgram(settings.nixBinDir + "/nix-env", false, envArgs);
runProgram(getNixBin("nix-env").string(), false, envArgs);
// Make the channels appear in nix-env.
struct stat st;
@ -244,7 +245,7 @@ static int main_nix_channel(int argc, char ** argv)
case cListGenerations:
if (!args.empty())
throw UsageError("'--list-generations' expects no arguments");
std::cout << runProgram(settings.nixBinDir + "/nix-env", false, {"--profile", profile, "--list-generations"}) << std::flush;
std::cout << runProgram(getNixBin("nix-env").string(), false, {"--profile", profile, "--list-generations"}) << std::flush;
break;
case cRollback:
if (args.size() > 1)
@ -256,7 +257,7 @@ static int main_nix_channel(int argc, char ** argv)
} else {
envArgs.push_back("--rollback");
}
runProgram(settings.nixBinDir + "/nix-env", false, envArgs);
runProgram(getNixBin("nix-env").string(), false, envArgs);
break;
}

1
src/nix-functional-tests Symbolic link
View file

@ -0,0 +1 @@
../tests/functional

View file

@ -438,14 +438,39 @@ struct CmdFlakeCheck : FlakeCommand
auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try {
#if 0
// FIXME
auto app = App(*state, v);
for (auto & i : app.context) {
auto [drvPathS, outputName] = NixStringContextElem::parse(i);
store->parseStorePath(drvPathS);
Activity act(*logger, lvlInfo, actUnknown, fmt("checking app '%s'", attrPath));
state->forceAttrs(v, pos, "");
if (auto attr = v.attrs()->get(state->symbols.create("type")))
state->forceStringNoCtx(*attr->value, attr->pos, "");
else
throw Error("app '%s' lacks attribute 'type'", attrPath);
if (auto attr = v.attrs()->get(state->symbols.create("program"))) {
if (attr->name == state->symbols.create("program")) {
NixStringContext context;
state->forceString(*attr->value, context, attr->pos, "");
}
} else
throw Error("app '%s' lacks attribute 'program'", attrPath);
if (auto attr = v.attrs()->get(state->symbols.create("meta"))) {
state->forceAttrs(*attr->value, attr->pos, "");
if (auto dAttr = attr->value->attrs()->get(state->symbols.create("description")))
state->forceStringNoCtx(*dAttr->value, dAttr->pos, "");
else
logWarning({
.msg = HintFmt("app '%s' lacks attribute 'meta.description'", attrPath),
});
} else
logWarning({
.msg = HintFmt("app '%s' lacks attribute 'meta'", attrPath),
});
for (auto & attr : *v.attrs()) {
std::string_view name(state->symbols[attr.name]);
if (name != "type" && name != "program" && name != "meta")
throw Error("app '%s' has unsupported attribute '%s'", attrPath, name);
}
#endif
} catch (Error & e) {
e.addTrace(resolve(pos), HintFmt("while checking the app definition '%s'", attrPath));
reportError(e);
@ -630,7 +655,7 @@ struct CmdFlakeCheck : FlakeCommand
const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) {
checkApp(
checkDerivation(
fmt("%s.%s", name, attr_name),
*attr.value, attr.pos);
};
@ -1252,8 +1277,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
}
j.emplace("type", "derivation");
j.emplace("name", name);
if (description)
j.emplace("description", *description);
j.emplace("description", description ? *description : "");
} else {
logger->cout("%s: %s '%s'",
headerPrefix,
@ -1341,12 +1365,19 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
(attrPath.size() == 3 && attrPathS[0] == "apps"))
{
auto aType = visitor.maybeGetAttr("type");
std::optional<std::string> description;
if (auto aMeta = visitor.maybeGetAttr(state->sMeta)) {
if (auto aDescription = aMeta->maybeGetAttr(state->sDescription))
description = aDescription->getString();
}
if (!aType || aType->getString() != "app")
state->error<EvalError>("not an app definition").debugThrow();
if (json) {
j.emplace("type", "app");
if (description)
j.emplace("description", *description);
} else {
logger->cout("%s: app", headerPrefix);
logger->cout("%s: app: " ANSI_BOLD "%s" ANSI_NORMAL, headerPrefix, description ? *description : "no description");
}
}

View file

@ -26,6 +26,8 @@ endif
nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libflake) $(INCLUDE_libmain) -I src/libcmd -I doc/manual $(INCLUDE_nix)
nix_CXXFLAGS += -DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\"
nix_LIBS = libexpr libmain libfetchers libflake libstore libutil libcmd
nix_LDFLAGS = $(THREAD_LDFLAGS) $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) $(LOWDOWN_LIBS)

View file

@ -19,6 +19,7 @@
#include "network-proxy.hh"
#include "eval-cache.hh"
#include "flake/flake.hh"
#include "self-exe.hh"
#include <sys/types.h>
#include <regex>
@ -366,6 +367,17 @@ void mainWrapped(int argc, char * * argv)
initGC();
flake::initLib(flakeSettings);
/* Set the build hook location
For builds we perform a self-invocation, so Nix has to be
self-aware. That is, it has to know where it is installed. We
don't think it's sentient.
*/
settings.buildHook.setDefault(Strings {
getNixBin({}).string(),
"__build-remote",
});
#if __linux__
if (isRootUser()) {
try {

View file

@ -33,6 +33,21 @@ subdir('build-utils-meson/threads')
subdir('build-utils-meson/export-all-symbols')
configdata = configuration_data()
fs = import('fs')
bindir = get_option('bindir')
if not fs.is_absolute(bindir)
bindir = get_option('prefix') / bindir
endif
configdata.set_quoted('NIX_BIN_DIR', bindir)
config_h = configure_file(
configuration : configdata,
output : 'config-nix-cli.hh',
)
add_project_arguments(
# TODO(Qyriad): Yes this is how the autoconf+Make system did it.
# It would be nice for our headers to be idempotent instead.
@ -42,15 +57,17 @@ add_project_arguments(
#'-include', 'config-fetchers.hh',
'-include', 'config-main.hh',
'-include', 'config-cmd.hh',
'-include', 'config-nix-cli.hh',
language : 'cpp',
)
subdir('build-utils-meson/diagnostics')
subdir('build-utils-meson/generate-header')
nix_sources = files(
nix_sources = [config_h] + files(
'add-to-store.cc',
'app.cc',
'self-exe.cc',
'build.cc',
'bundle.cc',
'cat.cc',
@ -175,3 +192,55 @@ this_exe = executable(
link_args: linker_export_flags,
install : true,
)
meson.override_find_program('nix', this_exe)
nix_symlinks = [
'nix-build',
'nix-channel',
'nix-collect-garbage',
'nix-copy-closure',
'nix-daemon',
'nix-env',
'nix-hash',
'nix-instantiate',
'nix-prefetch-url',
'nix-shell',
'nix-store',
]
foreach linkname : nix_symlinks
install_symlink(
linkname,
# TODO(Qyriad): should these continue to be relative symlinks?
pointing_to : 'nix',
install_dir : get_option('bindir'),
# The 'runtime' tag is what executables default to, which we want to emulate here.
install_tag : 'runtime'
)
t = custom_target(
command: ['ln', '-sf', fs.name(this_exe), '@OUTPUT@'],
output: linkname,
# TODO(Ericson2314): Don't do this once we have the `meson.override_find_program` working)
build_by_default: true
)
# TODO(Ericson3214): Dosen't yet work
#meson.override_find_program(linkname, t)
endforeach
install_symlink(
'build-remote',
pointing_to : '..' / '..'/ get_option('bindir') / 'nix',
install_dir : get_option('libexecdir') / 'nix',
# The 'runtime' tag is what executables default to, which we want to emulate here.
install_tag : 'runtime'
)
custom_target(
command: ['ln', '-sf', fs.name(this_exe), '@OUTPUT@'],
output: 'build-remote',
# TODO(Ericson2314): Don't do this once we have the `meson.override_find_program` working)
build_by_default: true
)
# TODO(Ericson3214): Dosen't yet work
#meson.override_find_program(linkname, t)

View file

@ -1,12 +1,32 @@
#include "eval.hh"
#include "eval-settings.hh"
#include "config-global.hh"
#include "globals.hh"
#include "command.hh"
#include "installable-value.hh"
#include "repl.hh"
#include "processes.hh"
#include "self-exe.hh"
namespace nix {
void runNix(Path program, const Strings & args,
const std::optional<std::string> & input = {})
{
auto subprocessEnv = getEnv();
subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue();
//isInteractive avoid grabling interactive commands
runProgram2(RunOptions {
.program = getNixBin(program).string(),
.args = args,
.environment = subprocessEnv,
.input = input,
.isInteractive = true,
});
return;
}
struct CmdRepl : RawInstallablesCommand
{
CmdRepl() {
@ -81,7 +101,8 @@ struct CmdRepl : RawInstallablesCommand
lookupPath,
openStore(),
state,
getValues
getValues,
runNix
);
repl->autoArgs = getAutoArgs(*repl->state);
repl->initEnv();

View file

@ -80,6 +80,7 @@ An app is specified by a flake output attribute named
apps.x86_64-linux.blender_2_79 = {
type = "app";
program = "${self.packages.x86_64-linux.blender_2_79}/bin/blender";
meta.description = "Run Blender, a free and open-source 3D creation suite.";
};
```
@ -90,4 +91,6 @@ The only supported attributes are:
* `program` (required): The full path of the executable to run. It
must reside in the Nix store.
* `meta.description` (optional): A description of the app.
)""

38
src/nix/self-exe.cc Normal file
View file

@ -0,0 +1,38 @@
#include "current-process.hh"
#include "file-system.hh"
#include "globals.hh"
#include "self-exe.hh"
namespace nix {
namespace fs = std::filesystem;
fs::path getNixBin(std::optional<std::string_view> binaryNameOpt)
{
auto getBinaryName = [&] { return binaryNameOpt ? *binaryNameOpt : "nix"; };
// If the environment variable is set, use it unconditionally
if (auto envOpt = getEnvNonEmpty("NIX_BIN_DIR"))
return fs::path{*envOpt} / std::string{getBinaryName()};
// Use some-times avaiable OS tricks to get to the path of this Nix, and try that
if (auto selfOpt = getSelfExe()) {
fs::path path{*selfOpt};
if (binaryNameOpt)
path = path.parent_path() / std::string{*binaryNameOpt};
if (fs::exists(path))
return path;
}
// If `nix` exists at the hardcoded fallback path, use it.
{
auto path = fs::path{NIX_BIN_DIR} / std::string{getBinaryName()};
if (fs::exists(path))
return path;
}
// return just the name, hoping the exe is on the `PATH`
return getBinaryName();
}
}

31
src/nix/self-exe.hh Normal file
View file

@ -0,0 +1,31 @@
#pragma once
///@file
#include <filesystem>
namespace nix {
/**
* Get a path to the given Nix binary.
*
* Normally, nix is installed according to `NIX_BIN_DIR`, which is set
* at compile time, but can be overridden.
*
* However, it may not have been installed at all. For example, if it's
* a static build, there's a good chance that it has been moved out of
* its installation directory. That makes `NIX_BIN_DIR` useless.
* Instead, we'll query the OS for the path to the current executable,
* using `getSelfExe()`.
*
* As a last resort, we resort to `PATH`. Hopefully we find a `nix`
* there that's compatible. If you're porting Nix to a new platform,
* that might be good enough for a while, but you'll want to improve
* `getSelfExe()` to work on your platform.
*
* @param binary_name the exact binary name we're looking up. Might be
* `nix-*` instead of `nix` for the legacy CLI commands. Optional to use
* current binary name.
*/
std::filesystem::path getNixBin(std::optional<std::string_view> binary_name = {});
}

View file

@ -9,6 +9,7 @@
#include "names.hh"
#include "progress-bar.hh"
#include "executable-path.hh"
#include "self-exe.hh"
using namespace nix;
@ -93,7 +94,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
{
Activity act(*logger, lvlInfo, actUnknown,
fmt("installing '%s' into profile '%s'...", store->printStorePath(storePath), profileDir));
runProgram(settings.nixBinDir + "/nix-env", false,
runProgram(getNixBin("nix-env").string(), false,
{"--profile", profileDir, "-i", store->printStorePath(storePath), "--no-sandbox"});
}
@ -103,7 +104,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
/* Return the profile in which Nix is installed. */
Path getProfileDir(ref<Store> store)
{
auto whereOpt = ExecutablePath::load().find(OS_STR("nix-env"));
auto whereOpt = ExecutablePath::load().findName(OS_STR("nix-env"));
if (!whereOpt)
throw Error("couldn't figure out how Nix is installed, so I can't upgrade it");
auto & where = *whereOpt;

View file

@ -5,7 +5,6 @@ use Nix::Store;
$version = "@PACKAGE_VERSION@";
$binDir = Nix::Store::getBinDir;
$storeDir = Nix::Store::getStoreDir;
%config = ();

View file

@ -24,7 +24,7 @@ our @EXPORT = qw(
hashPath hashFile hashString convertHash
signString checkSignature
getBinDir getStoreDir
getStoreDir
setVerbosity
);

View file

@ -424,11 +424,6 @@ StoreWrapper::addTempRoot(char * storePath)
}
SV * getBinDir()
PPCODE:
XPUSHs(sv_2mortal(newSVpv(settings.nixBinDir.c_str(), 0)));
SV * getStoreDir()
PPCODE:
XPUSHs(sv_2mortal(newSVpv(settings.nixStore.c_str(), 0)));