Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nix
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: e5ed74ec1e19
Choose a base ref
...
head repository: NixOS/nix
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 9181b208c973
Choose a head ref
  • 17 commits
  • 19 files changed
  • 3 contributors

Commits on May 17, 2021

  1. Split the parsing of an App and its resolving

    That way things (like `nix flake check`) can evaluate the `app` outputs
    without having to build anything
    thufschmitt committed May 17, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    rasendubi Oleksii Shmalko
    Copy the full SHA
    ca96f52 View commit details
  2. Source bashrc first in nix develop

    ~/.bashrc should be sourced first in the rc script so that PATH &
    other env vars give precedence over the bashrc PATH.
    
    Also, in my bashrc I alias rm as:
    
      alias rm='rm -Iv'
    
    To avoid running this alias (which shows ‘removed '/tmp/nix-shell.*'),
    we can just prefix rm with command.
    matthewbauer committed May 17, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5fd8cf7 View commit details

Commits on May 18, 2021

  1. Merge pull request #4825 from NixOS/split-app-parsing-and-resolving

    Split the parsing of an `App` and its resolving
    edolstra authored May 18, 2021
    Copy the full SHA
    72356a9 View commit details
  2. Merge pull request #4827 from matthewbauer/run-bashrc-first-in-nix-de…

    …velop
    
    Source bashrc first in nix develop
    edolstra authored May 18, 2021
    Copy the full SHA
    de77d1b View commit details
  3. Restore an accidentally suppressed negation

    Accidentally removed in ca96f52. This
    caused `nix run` to systematically fail with
    
    ```
    error: app program '/nix/store/…' is not in the Nix store
    ```
    thufschmitt committed May 18, 2021
    Copy the full SHA
    59d0de6 View commit details
  4. Merge pull request #4828 from NixOS/ca/fix-nix-run

    Restore an accidentally suppressed negation
    edolstra authored May 18, 2021
    Copy the full SHA
    57f321f View commit details
  5. Fix extra slash in canonPath output

    When you have a symlink like:
    
      /tmp -> ./private/tmp
    
    you need to resolve ./private/tmp relative to /tmp’s dir: ‘/’. Unlike
    any other path output by dirOf, / ends with a slash. We don’t want
    trailing slashes here since we will append another slash in the next
    comoponent, so clear s like we would if it was a symlink to an absoute
    path.
    
    This should fix at least part of the issue in
    #4822, will need confirmation that
    it actually fixes the problem to close though.
    
    Introduced in f3f2287.
    matthewbauer committed May 18, 2021
    Copy the full SHA
    3d90ab9 View commit details

Commits on May 19, 2021

  1. Merge pull request #4831 from matthewbauer/fix-extra-slash-in-canon-path

    Fix extra slash in canonPath output
    edolstra authored May 19, 2021
    Copy the full SHA
    7234cf3 View commit details
  2. Extract a generic computeClosure function

    Move the `closure` logic of `computeFSClosure` to its own (templated) function.
    
    This doesn’t bring much by itself (except for the ability to properly
    test the “closure” functionality independently from the rest), but it
    allows reusing it (in particular for the realisations which will require
    a very similar closure computation)
    thufschmitt committed May 19, 2021
    Copy the full SHA
    1845588 View commit details
  3. Always send the realisations as JSON

    Align all the worker protocol with `buildDerivation` which inlines the
    realisations as one opaque json blob.
    That way we don’t have to bother changing the remote store protocol
    when the definition of `Realisation` changes, as long as we keep the
    json backwards-compatible
    thufschmitt committed May 19, 2021
    Copy the full SHA
    a841686 View commit details
  4. Copy the full SHA
    05dde12 View commit details
  5. Add a dependencies field to DrvOutputInfo

    Currently never used, nor set but will be useful shortly
    thufschmitt committed May 19, 2021
    Copy the full SHA
    3c72e34 View commit details
  6. Copy the full SHA
    cb16dad View commit details
  7. Copy the full SHA
    36ebb2c View commit details
  8. Add a method to compute the closure of a realisation

    Only considers the closure in term of `Realisation`, ignores all the
    opaque inputs.
    
    Dunno whether that’s the nicest solution, need to think it through a bit
    thufschmitt committed May 19, 2021
    Copy the full SHA
    9160c39 View commit details
  9. Copy the full SHA
    be29c86 View commit details
  10. Make copyPaths copy the whole realisations closure

    Otherwise registering the realisations on the remote side might fail as
    it now expects a complete closure
    thufschmitt committed May 19, 2021
    Copy the full SHA
    9181b20 View commit details
8 changes: 7 additions & 1 deletion src/libcmd/installables.hh
Original file line number Diff line number Diff line change
@@ -23,6 +23,12 @@ struct App
// FIXME: add args, sandbox settings, metadata, ...
};

struct UnresolvedApp
{
App unresolved;
App resolve(ref<Store>);
};

struct Installable
{
virtual ~Installable() { }
@@ -33,7 +39,7 @@ struct Installable

DerivedPath toDerivedPath();

App toApp(EvalState & state);
UnresolvedApp toApp(EvalState & state);

virtual std::pair<Value *, Pos> toValue(EvalState & state)
{
1 change: 1 addition & 0 deletions src/libstore/build/derivation-goal.cc
Original file line number Diff line number Diff line change
@@ -927,6 +927,7 @@ void DerivationGoal::resolvedFinished() {
auto newRealisation = *realisation;
newRealisation.id = DrvOutput{initialOutputs.at(wantedOutput).outputHash, wantedOutput};
newRealisation.signatures.clear();
newRealisation.drvOutputDeps = drvOutputReferences(worker.store, *drv, realisation->outPath);
signRealisation(newRealisation);
worker.store.registerDrvOutput(newRealisation);
} else {
11 changes: 10 additions & 1 deletion src/libstore/ca-specific-schema.sql
Original file line number Diff line number Diff line change
@@ -3,10 +3,19 @@
-- is enabled

create table if not exists Realisations (
id integer primary key autoincrement not null,
drvPath text not null,
outputName text not null, -- symbolic output id, usually "out"
outputPath integer not null,
signatures text, -- space-separated list
primary key (drvPath, outputName),
foreign key (outputPath) references ValidPaths(id) on delete cascade
);

create index if not exists IndexRealisations on Realisations(drvPath, outputName);

create table if not exists RealisationsRefs (
referrer integer not null,
realisationReference integer,
foreign key (referrer) references Realisations(id) on delete cascade,
foreign key (realisationReference) references Realisations(id) on delete restrict
);
25 changes: 18 additions & 7 deletions src/libstore/daemon.cc
Original file line number Diff line number Diff line change
@@ -885,10 +885,15 @@ static void performOp(TunnelLogger * logger, ref<Store> store,

case wopRegisterDrvOutput: {
logger->startWork();
auto outputId = DrvOutput::parse(readString(from));
auto outputPath = StorePath(readString(from));
store->registerDrvOutput(Realisation{
.id = outputId, .outPath = outputPath});
if (GET_PROTOCOL_MINOR(clientVersion) < 31) {
auto outputId = DrvOutput::parse(readString(from));
auto outputPath = StorePath(readString(from));
store->registerDrvOutput(Realisation{
.id = outputId, .outPath = outputPath});
} else {
auto realisation = worker_proto::read(*store, from, Phantom<Realisation>());
store->registerDrvOutput(realisation);
}
logger->stopWork();
break;
}
@@ -898,9 +903,15 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
auto outputId = DrvOutput::parse(readString(from));
auto info = store->queryRealisation(outputId);
logger->stopWork();
std::set<StorePath> outPaths;
if (info) outPaths.insert(info->outPath);
worker_proto::write(*store, to, outPaths);
if (GET_PROTOCOL_MINOR(clientVersion) < 31) {
std::set<StorePath> outPaths;
if (info) outPaths.insert(info->outPath);
worker_proto::write(*store, to, outPaths);
} else {
std::set<Realisation> realisations;
if (info) realisations.insert(*info);
worker_proto::write(*store, to, realisations);
}
break;
}

61 changes: 49 additions & 12 deletions src/libstore/local-store.cc
Original file line number Diff line number Diff line change
@@ -59,6 +59,8 @@ struct LocalStore::State::Stmts {
SQLiteStmt QueryAllRealisedOutputs;
SQLiteStmt QueryPathFromHashPart;
SQLiteStmt QueryValidPaths;
SQLiteStmt QueryRealisationRealisationReferences;
SQLiteStmt AddRealisationRealisationReference;
};

int getSchema(Path schemaPath)
@@ -316,7 +318,7 @@ LocalStore::LocalStore(const Params & params)
)");
state->stmts->QueryRealisedOutput.create(state->db,
R"(
select Output.path, Realisations.signatures from Realisations
select Realisations.id, Output.path, Realisations.signatures from Realisations
inner join ValidPaths as Output on Output.id = Realisations.outputPath
where drvPath = ? and outputName = ?
;
@@ -328,6 +330,19 @@ LocalStore::LocalStore(const Params & params)
where drvPath = ?
;
)");
state->stmts->QueryRealisationRealisationReferences.create(state->db,
R"(
select drvPath, outputName from Realisations
join RealisationsRefs on realisationReference = Realisations.id
where referrer = ?;
)");
state->stmts->AddRealisationRealisationReference.create(state->db,
R"(
insert or replace into RealisationsRefs (referrer, realisationReference)
values (
?,
(select id from Realisations where drvPath = ? and outputName = ?));
)");
}
}

@@ -666,13 +681,17 @@ void LocalStore::registerDrvOutput(const Realisation & info)
settings.requireExperimentalFeature("ca-derivations");
auto state(_state.lock());
retrySQLite<void>([&]() {
state->stmts->RegisterRealisedOutput.use()
(info.id.strHash())
(info.id.outputName)
(printStorePath(info.outPath))
(concatStringsSep(" ", info.signatures))
state->stmts->RegisterRealisedOutput
.use()(info.id.strHash())(info.id.outputName)(printStorePath(
info.outPath))(concatStringsSep(" ", info.signatures))
.exec();
});
uint64_t myId = state->db.getLastInsertedRowId();
for (auto& outputId : info.drvOutputDeps) {
state->stmts->AddRealisationRealisationReference
.use()(myId)(outputId.strHash())(outputId.outputName)
.exec();
}
}

void LocalStore::cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output)
@@ -1670,14 +1689,32 @@ std::optional<const Realisation> LocalStore::queryRealisation(
typedef std::optional<const Realisation> Ret;
return retrySQLite<Ret>([&]() -> Ret {
auto state(_state.lock());
auto use(state->stmts->QueryRealisedOutput.use()(id.strHash())(
id.outputName));
if (!use.next())
auto useQueryRealisedOutput(state->stmts->QueryRealisedOutput.use()(
id.strHash())(id.outputName));
if (!useQueryRealisedOutput.next())
return std::nullopt;
auto outputPath = parseStorePath(use.getStr(0));
auto signatures = tokenizeString<StringSet>(use.getStr(1));
auto realisationDbId = useQueryRealisedOutput.getInt(0);
auto outputPath = parseStorePath(useQueryRealisedOutput.getStr(1));
auto signatures =
tokenizeString<StringSet>(useQueryRealisedOutput.getStr(2));

std::set<DrvOutput> drvOutputDeps;
auto useRealisationRefs(
state->stmts->QueryRealisationRealisationReferences.use()(
realisationDbId));
while (useRealisationRefs.next())
drvOutputDeps.insert(DrvOutput{
Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)),
useRealisationRefs.getStr(1),
}
);

return Ret{Realisation{
.id = id, .outPath = outputPath, .signatures = signatures}};
.id = id,
.outPath = outputPath,
.signatures = signatures,
.drvOutputDeps = drvOutputDeps,
}};
});
}
} // namespace nix
185 changes: 101 additions & 84 deletions src/libstore/misc.cc
Original file line number Diff line number Diff line change
@@ -6,98 +6,73 @@
#include "thread-pool.hh"
#include "topo-sort.hh"
#include "callback.hh"
#include "closure.hh"

namespace nix {


void Store::computeFSClosure(const StorePathSet & startPaths,
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
{
struct State
{
size_t pending;
StorePathSet & paths;
std::exception_ptr exc;
};

Sync<State> state_(State{0, paths_, 0});

std::function<void(const StorePath &)> enqueue;

std::condition_variable done;

enqueue = [&](const StorePath & path) -> void {
{
auto state(state_.lock());
if (state->exc) return;
if (!state->paths.insert(path).second) return;
state->pending++;
}

queryPathInfo(path, {[&](std::future<ref<const ValidPathInfo>> fut) {
// FIXME: calls to isValidPath() should be async

try {
auto info = fut.get();

if (flipDirection) {

StorePathSet referrers;
queryReferrers(path, referrers);
for (auto & ref : referrers)
if (ref != path)
enqueue(ref);

if (includeOutputs)
for (auto & i : queryValidDerivers(path))
enqueue(i);

if (includeDerivers && path.isDerivation())
for (auto & i : queryDerivationOutputs(path))
if (isValidPath(i) && queryPathInfo(i)->deriver == path)
enqueue(i);

} else {

for (auto & ref : info->references)
if (ref != path)
enqueue(ref);

if (includeOutputs && path.isDerivation())
for (auto & i : queryDerivationOutputs(path))
if (isValidPath(i)) enqueue(i);

if (includeDerivers && info->deriver && isValidPath(*info->deriver))
enqueue(*info->deriver);

}

{
auto state(state_.lock());
assert(state->pending);
if (!--state->pending) done.notify_one();
}

} catch (...) {
auto state(state_.lock());
if (!state->exc) state->exc = std::current_exception();
assert(state->pending);
if (!--state->pending) done.notify_one();
};
}});
};

for (auto & startPath : startPaths)
enqueue(startPath);

{
auto state(state_.lock());
while (state->pending) state.wait(done);
if (state->exc) std::rethrow_exception(state->exc);
}
std::function<std::set<StorePath>(const StorePath & path, std::future<ref<const ValidPathInfo>> &)> queryDeps;
if (flipDirection)
queryDeps = [&](const StorePath& path,
std::future<ref<const ValidPathInfo>> & fut) {
StorePathSet res;
StorePathSet referrers;
queryReferrers(path, referrers);
for (auto& ref : referrers)
if (ref != path)
res.insert(ref);

if (includeOutputs)
for (auto& i : queryValidDerivers(path))
res.insert(i);

if (includeDerivers && path.isDerivation())
for (auto& i : queryDerivationOutputs(path))
if (isValidPath(i) && queryPathInfo(i)->deriver == path)
res.insert(i);
return res;
};
else
queryDeps = [&](const StorePath& path,
std::future<ref<const ValidPathInfo>> & fut) {
StorePathSet res;
auto info = fut.get();
for (auto& ref : info->references)
if (ref != path)
res.insert(ref);

if (includeOutputs && path.isDerivation())
for (auto& i : queryDerivationOutputs(path))
if (isValidPath(i))
res.insert(i);

if (includeDerivers && info->deriver && isValidPath(*info->deriver))
res.insert(*info->deriver);
return res;
};

computeClosure<StorePath>(
startPaths, paths_,
[&](const StorePath& path,
std::function<void(std::promise<std::set<StorePath>>&)>
processEdges) {
std::promise<std::set<StorePath>> promise;
std::function<void(std::future<ref<const ValidPathInfo>>)>
getDependencies =
[&](std::future<ref<const ValidPathInfo>> fut) {
try {
promise.set_value(queryDeps(path, fut));
} catch (...) {
promise.set_exception(std::current_exception());
}
};
queryPathInfo(path, getDependencies);
processEdges(promise);
});
}


void Store::computeFSClosure(const StorePath & startPath,
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
{
@@ -279,5 +254,47 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
}});
}

std::set<DrvOutput> drvOutputReferences(
const std::set<Realisation> inputRealisations,
const StorePathSet pathReferences)
{
std::set<DrvOutput> res;

std::map<StorePath, std::set<DrvOutput>> inputsByOutputPath;
for (const auto & input : inputRealisations)
inputsByOutputPath[input.outPath].insert(input.id);

for (const auto & path : pathReferences) {
auto theseInputs = inputsByOutputPath[path];
res.insert(theseInputs.begin(), theseInputs.end());
}

return res;
}

std::set<DrvOutput> drvOutputReferences(
Store & store,
const Derivation & drv,
const StorePath & outputPath)
{
std::set<Realisation> inputRealisations;

for (const auto& [inputDrv, outputNames] : drv.inputDrvs) {
auto outputHashes =
staticOutputHashes(store, store.readDerivation(inputDrv));
for (const auto& outputName : outputNames) {
auto thisRealisation = store.queryRealisation(
DrvOutput{outputHashes.at(outputName), outputName});
if (!thisRealisation)
throw Error(
"output '%s' of derivation '%s' isn’t built", outputName,
store.printStorePath(inputDrv));
inputRealisations.insert(*thisRealisation);
}
}

auto info = store.queryPathInfo(outputPath);

return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references);
}
}
Loading