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:
commit
58ef129502
99 changed files with 1449 additions and 787 deletions
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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**
|
||||
>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() + "»");
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)\"
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
1
src/nix-functional-tests
Symbolic link
|
@ -0,0 +1 @@
|
|||
../tests/functional
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
38
src/nix/self-exe.cc
Normal 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
31
src/nix/self-exe.hh
Normal 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 = {});
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -5,7 +5,6 @@ use Nix::Store;
|
|||
|
||||
$version = "@PACKAGE_VERSION@";
|
||||
|
||||
$binDir = Nix::Store::getBinDir;
|
||||
$storeDir = Nix::Store::getStoreDir;
|
||||
|
||||
%config = ();
|
||||
|
|
|
@ -24,7 +24,7 @@ our @EXPORT = qw(
|
|||
|
||||
hashPath hashFile hashString convertHash
|
||||
signString checkSignature
|
||||
getBinDir getStoreDir
|
||||
getStoreDir
|
||||
setVerbosity
|
||||
);
|
||||
|
||||
|
|
|
@ -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)));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue