diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index 68b642d86ce..905401be07d 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -178,6 +178,11 @@ StorePath * nix_store_path_clone(const StorePath * p) return new StorePath{p->path}; } +nix_derivation * nix_derivation_clone(const nix_derivation * d) +{ + return new nix_derivation{d->drv}; +} + nix_derivation * nix_derivation_from_json(nix_c_context * context, Store * store, const char * json) { if (context) @@ -194,6 +199,45 @@ nix_derivation * nix_derivation_from_json(nix_c_context * context, Store * store NIXC_CATCH_ERRS_NULL } +nix_err nix_derivation_to_json( + nix_c_context * context, const nix_derivation * drv, nix_get_string_callback callback, void * userdata) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto result = drv->drv.toJSON().dump(); + if (callback) { + callback(result.data(), result.size(), userdata); + } + } + NIXC_CATCH_ERRS +} + +nix_err nix_derivation_make_outputs( + nix_c_context * context, + Store * store, + const char * json, + void (*callback)(void * userdata, const char * output_name, const char * path), + void * userdata) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto drv = static_cast(nlohmann::json::parse(json)); + auto hashesModulo = hashDerivationModulo(*store->ptr, drv, true); + + for (auto & output : drv.outputs) { + nix::Hash h = hashesModulo.hashes.at(output.first); + auto outPath = store->ptr->makeOutputPath(output.first, h, drv.name); + + if (callback) { + callback(userdata, output.first.c_str(), store->ptr->printStorePath(outPath).c_str()); + } + } + } + NIXC_CATCH_ERRS +} + StorePath * nix_add_derivation(nix_c_context * context, Store * store, nix_derivation * derivation) { if (context) @@ -218,4 +262,96 @@ nix_err nix_store_copy_closure(nix_c_context * context, Store * srcStore, Store NIXC_CATCH_ERRS } +nix_err nix_store_drv_from_path( + nix_c_context * context, + Store * store, + const StorePath * path, + void (*callback)(void * userdata, const nix_derivation * drv), + void * userdata) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::Derivation drv = store->ptr->derivationFromPath(path->path); + if (callback) { + const nix_derivation tmp{drv}; + callback(userdata, &tmp); + } + } + NIXC_CATCH_ERRS +} + +nix_err nix_store_query_path_info( + nix_c_context * context, + Store * store, + const StorePath * store_path, + void * userdata, + nix_get_string_callback callback) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto info = store->ptr->queryPathInfo(store_path->path); + if (callback) { + auto result = info->toJSON(store->ptr->config, true, nix::HashFormat::Nix32).dump(); + callback(result.data(), result.size(), userdata); + } + } + NIXC_CATCH_ERRS +} + +nix_err nix_store_build_paths( + nix_c_context * context, + Store * store, + const StorePath ** store_paths, + unsigned int num_store_paths, + void (*callback)(void * userdata, const char * path, const char * result), + void * userdata) +{ + if (context) + context->last_err_code = NIX_OK; + try { + std::vector derived_paths; + for (size_t i = 0; i < num_store_paths; i++) { + const StorePath * store_path = store_paths[i]; + derived_paths.push_back(nix::SingleDerivedPath::Opaque{store_path->path}); + } + + auto results = store->ptr->buildPathsWithResults(derived_paths); + for (auto & result : results) { + if (callback) { + nlohmann::json json; + nix::to_json(json, result); + callback(userdata, result.path.to_string(store->ptr->config).c_str(), json.dump().c_str()); + } + } + } + NIXC_CATCH_ERRS +} + +nix_err nix_derivation_get_outputs_and_optpaths( + nix_c_context * context, + const nix_derivation * drv, + const Store * store, + void (*callback)(void * userdata, const char * name, const StorePath * path), + void * userdata) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto value = drv->drv.outputsAndOptPaths(store->ptr->config); + if (callback) { + for (const auto & [name, result] : value) { + if (auto store_path = result.second) { + const StorePath tmp_path{*store_path}; + callback(userdata, name.c_str(), &tmp_path); + } else { + callback(userdata, name.c_str(), nullptr); + } + } + } + } + NIXC_CATCH_ERRS +} + } // extern "C" diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index e76e376b480..c8b54995180 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -218,6 +218,33 @@ nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_cal */ nix_derivation * nix_derivation_from_json(nix_c_context * context, Store * store, const char * json); +/** + * @brief Gets the derivation as a JSON string + * + * @param[out] context Optional, stores error information + * @param[in] drv The derivation + * @param[in] callback Called with the JSON string + * @param[in] userdata Arbitrary data passed to the callback + */ +nix_err nix_derivation_to_json( + nix_c_context * context, const nix_derivation * drv, nix_get_string_callback callback, void * userdata); + +/** + * @brief Hashes the derivation and gives the output paths + * + * @param[in] context Optional, stores error information. + * @param[in] store nix store reference. + * @param[in] json JSON of the derivation as a string. + * @param[in] callback Called for every output to provide the output path. + * @param[in] userdata User data to pass to the callback. + */ +nix_err nix_derivation_make_outputs( + nix_c_context * context, + Store * store, + const char * json, + void (*callback)(void * userdata, const char * output_name, const char * path), + void * userdata); + /** * @brief Add the given `nix_derivation` to the given store * @@ -235,6 +262,14 @@ StorePath * nix_add_derivation(nix_c_context * context, Store * store, nix_deriv */ void nix_derivation_free(nix_derivation * drv); +/** + * @brief Copy a `nix_derivation` + * + * @param[in] d the derivation to copy + * @return a new `nix_derivation` + */ +nix_derivation * nix_derivation_clone(const nix_derivation * d); + /** * @brief Copy the closure of `path` from `srcStore` to `dstStore`. * @@ -245,6 +280,79 @@ void nix_derivation_free(nix_derivation * drv); */ nix_err nix_store_copy_closure(nix_c_context * context, Store * srcStore, Store * dstStore, StorePath * path); +/** + * @brief Returns the derivation associated with the store path + * + * @note The callback borrows the Derivation only for the duration of the call. + * + * @param[out] context Optional, stores error information + * @param[in] store The nix store + * @param[in] path The nix store path + * @param[in] callback The callback to call + * @param[in] userdata The userdata to pass to the callback + */ +nix_err nix_store_drv_from_path( + nix_c_context * context, + Store * store, + const StorePath * path, + void (*callback)(void * userdata, const nix_derivation * drv), + void * userdata); + +/** + * @brief Queries for the nix store path info JSON. + * + * @param[out] context Optional, stores error information + * @param[in] store nix store reference + * @param[in] path A store path + * @param[in] userdata The data to pass to the callback + * @param[in] callback Called for when the path info is resolved + */ +nix_err nix_store_query_path_info( + nix_c_context * context, + Store * store, + const StorePath * store_path, + void * userdata, + nix_get_string_callback callback); + +/** + * @brief Builds the paths, if they are a derivation then they get built. + * + * @note Path and result for the callback only exist for the lifetime of + * the call. Result is a string containing the build result in JSON. + * + * @param[out] context Optional, stores error information + * @param[in] store nix store reference + * @param[in] store_paths Pointer to list of nix store paths + * @param[in] num_store_paths Number of nix store paths + * @param[in] callback The callback to trigger for build results + * @param[in] userdata User data to pass to the callback + */ +nix_err nix_store_build_paths( + nix_c_context * context, + Store * store, + const StorePath ** store_paths, + unsigned int num_store_paths, + void (*callback)(void * userdata, const char * path, const char * result), + void * userdata); + +/** + * @brief Iterate and get all of the derivation outputs and their store paths. + * + * @note The callback borrows the DerivationOutput and StorePath only for the duration of the call. + * + * @param[out] context Optional, stores error information + * @param[in] drv The derivation + * @param[in] store The nix store + * @param[in] callback The function to call on every output and store path + * @param[in] userdata The userdata to pass to the callback + */ +nix_err nix_derivation_get_outputs_and_optpaths( + nix_c_context * context, + const nix_derivation * drv, + const Store * store, + void (*callback)(void * userdata, const char * name, const StorePath * path), + void * userdata); + // cffi end #ifdef __cplusplus } diff --git a/src/libstore/build-result.cc b/src/libstore/build-result.cc index ecbd27b4931..850a86d75ba 100644 --- a/src/libstore/build-result.cc +++ b/src/libstore/build-result.cc @@ -1,5 +1,7 @@ #include "nix/store/build-result.hh" +#include + namespace nix { bool BuildResult::operator==(const BuildResult &) const noexcept = default; @@ -11,4 +13,40 @@ std::strong_ordering BuildResult::Success::operator<=>(const BuildResult::Succes bool BuildResult::Failure::operator==(const BuildResult::Failure &) const noexcept = default; std::strong_ordering BuildResult::Failure::operator<=>(const BuildResult::Failure &) const noexcept = default; +void to_json(nlohmann::json & json, const BuildResult & buildResult) +{ + json = nlohmann::json::object(); + json["status"] = BuildResult::statusToString(buildResult.status()); + if (buildResult.errorMsg() != "") + json["errorMsg"] = buildResult.errorMsg(); + if (buildResult.timesBuilt) + json["timesBuilt"] = buildResult.timesBuilt; + if (buildResult.startTime) + json["startTime"] = buildResult.startTime; + if (buildResult.stopTime) + json["stopTime"] = buildResult.stopTime; + + auto fail = buildResult.tryGetFailure(); + if (fail != nullptr) { + if (fail->isNonDeterministic) + json["isNonDeterministic"] = fail->isNonDeterministic; + } +} + +void to_json(nlohmann::json & json, const KeyedBuildResult & buildResult) +{ + json = nlohmann::json((const BuildResult &) buildResult); + auto path = nlohmann::json::object(); + std::visit( + overloaded{ + [&](const DerivedPathOpaque & opaque) { path["opaque"] = opaque.path.to_string(); }, + [&](const DerivedPathBuilt & drv) { + path["drvPath"] = drv.drvPath->getBaseStorePath().to_string(); + path["outputs"] = drv.outputs.to_string(); + }, + }, + buildResult.path.raw()); + json["path"] = std::move(path); +} + } // namespace nix diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 5dfc334a80b..c40ae98591e 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -374,7 +374,14 @@ Goal::Done DerivationGoal::doneFailure(BuildError ex) worker.updateProgress(); - return amDone(ecFailed, {std::move(ex)}); + auto traceBuiltOutputsFile = getEnv("_NIX_TRACE_BUILT_OUTPUTS").value_or(""); + if (traceBuiltOutputsFile != "") { + std::fstream fs; + fs.open(traceBuiltOutputsFile, std::fstream::out); + fs << worker.store.printStorePath(drvPath) << "\t" << buildResult.toString() << std::endl; + } + + return amDone(buildResult.tryGetSuccess() != nullptr ? ecSuccess : ecFailed, std::move(ex)); } } // namespace nix diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc index d16e530a42c..a2cc4a38b82 100644 --- a/src/libstore/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -8,6 +8,8 @@ #include +#include + namespace nix { PathSubstitutionGoal::PathSubstitutionGoal( diff --git a/src/libstore/include/nix/store/build-result.hh b/src/libstore/include/nix/store/build-result.hh index 0446c40388b..8eb80b185df 100644 --- a/src/libstore/include/nix/store/build-result.hh +++ b/src/libstore/include/nix/store/build-result.hh @@ -8,6 +8,8 @@ #include "nix/store/derived-path.hh" #include "nix/store/realisation.hh" +#include + namespace nix { struct BuildResult @@ -122,6 +124,63 @@ struct BuildResult return std::get_if(&self.inner); } + static std::string_view statusToString(uint8_t status) + { + switch (status) { + case Success::Status::Built: + return "Built"; + case Success::Status::Substituted: + return "Substituted"; + case Success::Status::AlreadyValid: + return "AlreadyValid"; + case Failure::Status::PermanentFailure: + return "PermanentFailure"; + case Failure::Status::InputRejected: + return "InputRejected"; + case Failure::Status::OutputRejected: + return "OutputRejected"; + case Failure::Status::TransientFailure: + return "TransientFailure"; + case Failure::Status::CachedFailure: + return "CachedFailure"; + case Failure::Status::TimedOut: + return "TimedOut"; + case Failure::Status::MiscFailure: + return "MiscFailure"; + case Failure::Status::DependencyFailed: + return "DependencyFailed"; + case Failure::Status::LogLimitExceeded: + return "LogLimitExceeded"; + case Failure::Status::NotDeterministic: + return "NotDeterministic"; + case Success::Status::ResolvesToAlreadyValid: + return "ResolvesToAlreadyValid"; + case Failure::Status::NoSubstituters: + return "NoSubstituters"; + default: + return "Unknown"; + }; + } + + uint8_t status(this auto & self) + { + auto fail = self.tryGetFailure(); + auto success = self.tryGetSuccess(); + return fail != nullptr ? fail->status : success->status; + } + + std::string errorMsg(this auto & self) + { + auto fail = self.tryGetFailure(); + return fail != nullptr ? fail->errorMsg : ""; + } + + std::string toString(this auto & self) + { + auto msg = self.errorMsg(); + return std::string(statusToString(self.status())) + ((msg == "") ? "" : " : " + msg); + } + /** * How many times this build was performed. */ @@ -172,6 +231,11 @@ struct KeyedBuildResult : BuildResult , path(std::move(path)) { } + + nlohmann::json toJSON(Store & store) const; }; +void to_json(nlohmann::json & json, const BuildResult & buildResult); +void to_json(nlohmann::json & json, const KeyedBuildResult & buildResult); + } // namespace nix diff --git a/src/libutil/include/nix/util/logging.hh b/src/libutil/include/nix/util/logging.hh index 500d443e6e2..a7af774a7f1 100644 --- a/src/libutil/include/nix/util/logging.hh +++ b/src/libutil/include/nix/util/logging.hh @@ -39,6 +39,8 @@ typedef enum { resSetExpected = 106, resPostBuildLogLine = 107, resFetchStatus = 108, + resHashMismatch = 109, + resBuildResult = 110, } ResultType; typedef uint64_t ActivityId;