aboutsummaryrefslogtreecommitdiff
path: root/src/nix/bundle.cc
blob: 9fc2656bb1dc3a5131f8e87f153c419d9d3bc088 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
#include "fs-accessor.hh"

using namespace nix;

struct CmdBundle : InstallableCommand
{
    std::string bundler = "github:matthewbauer/nix-bundle";
    std::optional<Path> outLink;

    CmdBundle()
    {
        addFlag({
            .longName = "bundler",
            .description = fmt("Use a custom bundler instead of the default (`%s`).", bundler),
            .labels = {"flake-url"},
            .handler = {&bundler},
            .completer = {[&](size_t, std::string_view prefix) {
                completeFlakeRef(getStore(), prefix);
            }}
        });

        addFlag({
            .longName = "out-link",
            .shortName = 'o',
            .description = "Override the name of the symlink to the build result. It defaults to the base name of the app.",
            .labels = {"path"},
            .handler = {&outLink},
            .completer = completePath
        });

    }

    std::string description() override
    {
        return "bundle an application so that it works outside of the Nix store";
    }

    std::string doc() override
    {
        return
          #include "bundle.md"
          ;
    }

    Category category() override { return catSecondary; }

    Strings getDefaultFlakeAttrPaths() override
    {
        Strings res{"defaultApp." + settings.thisSystem.get()};
        for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths())
            res.push_back(s);
        return res;
    }

    Strings getDefaultFlakeAttrPathPrefixes() override
    {
        Strings res{"apps." + settings.thisSystem.get() + "."};
        for (auto & s : SourceExprCommand::getDefaultFlakeAttrPathPrefixes())
            res.push_back(s);
        return res;
    }

    void run(ref<Store> store) override
    {
        auto evalState = getEvalState();

        auto app = installable->toApp(*evalState).resolve(getEvalStore(), store);

        auto [progFlakeRef, progName] = parseFlakeRefWithFragment(installable->what(), absPath("."));
        const flake::LockFlags lockFlagsProg{ .writeLockFile = false };
        auto programInstallable = InstallableFlake(this,
            evalState, std::move(progFlakeRef),
            Strings{progName == "" ? "defaultPackage" : progName},
            Strings({"packages."+settings.thisSystem.get()+".","legacyPackages."+settings.thisSystem.get()+"."}), lockFlagsProg);
        auto val = programInstallable.toValue(*evalState).first;

        auto [bundlerFlakeRef, bundlerName] = parseFlakeRefWithFragment(bundler, absPath("."));
        const flake::LockFlags lockFlags{ .writeLockFile = false };
        auto bundler = InstallableFlake(this,
            evalState, std::move(bundlerFlakeRef),
            Strings{bundlerName == "" ? "defaultBundler" : bundlerName},
            Strings({"bundlers."}), lockFlags);

        auto attrs = evalState->buildBindings(2);

        Value & prog = *evalState->allocAttr(*arg, evalState->symbols.create("program"));
        evalState->mkAttrs(prog,val->attrs->size());
        for (auto &j : *(val->attrs)){
            prog.attrs->push_back(j);
        }
        prog.attrs->sort();

        attrs.alloc("system").mkString(settings.thisSystem.get());

        auto vRes = evalState->allocValue();
        evalState->callFunction(
            *bundler.toValue(*evalState).first,
            evalState->allocValue()->mkAttrs(attrs),
            *vRes, noPos);

        if (!evalState->isDerivation(*vRes))
            throw Error("the bundler '%s' does not produce a derivation", bundler.what());

        auto attr1 = vRes->attrs->get(evalState->sDrvPath);
        if (!attr1)
            throw Error("the bundler '%s' does not produce a derivation", bundler.what());

        PathSet context2;
        StorePath drvPath = store->parseStorePath(evalState->coerceToPath(*attr1->pos, *attr1->value, context2));

        auto attr2 = vRes->attrs->get(evalState->sOutPath);
        if (!attr2)
            throw Error("the bundler '%s' does not produce a derivation", bundler.what());

        StorePath outPath = store->parseStorePath(evalState->coerceToPath(*attr2->pos, *attr2->value, context2));

        store->buildPaths({ DerivedPath::Built { drvPath } });

        auto outPathS = store->printStorePath(outPath);

        if (!outLink)
            outLink = baseNameOf(app.program);

        store.dynamic_pointer_cast<LocalFSStore>()->addPermRoot(outPath, absPath(*outLink));
    }
};

static auto r2 = registerCommand<CmdBundle>("bundle");