schema compile FEATURE introduction of dependency sets
Dependency sets are sets of modules that (may)
depend on each other. In practice that means that
whenever a module from the dep set is (re)compiled,
all the other implemented modules in the dep set
must also be (re)compiled.
Also, every compilation is now split into compiling
each dep set separately since all the modules cannot
depend on each other in any way. This resulted in
splitting unres into global one (that is actually
used only for reverting any changes) and specific
unres for dep sets, which is always resolved at the
end of a single dep set compilation.
Finally, functions for checking that a module needs
to be compiled or recompiled were added. These allow
to split dep sets and not drag a redundant dependent
module (such as ietf-inet-types or ietf-yang-types
that are imported in almost all modules, which would
always result in creating a single dep set).
diff --git a/src/context.c b/src/context.c
index cdbce9c..e004f65 100644
--- a/src/context.c
+++ b/src/context.c
@@ -204,12 +204,20 @@
ret = _lys_set_implemented(mod, features, &unres);
LY_CHECK_GOTO(ret, cleanup);
+ if (!(ctx->flags & LY_CTX_EXPLICIT_COMPILE)) {
+ /* create dep set for the module and mark all the modules that will be (re)compiled */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_to_compile(ctx, &unres.dep_sets, mod), cleanup);
+
+ /* (re)compile the whole dep set */
+ LY_CHECK_GOTO(ret = lys_compile_dep_set_r(ctx, unres.dep_sets.objs[0], &unres), cleanup);
+ }
+
cleanup:
if (ret) {
- lys_compile_unres_glob_revert(ctx, &unres);
+ lys_unres_glob_revert(ctx, &unres);
mod = NULL;
}
- lys_compile_unres_glob_erase(ctx, &unres, 0);
+ lys_unres_glob_erase(&unres);
return mod;
}
@@ -288,9 +296,6 @@
}
}
- /* resolve global unres */
- LY_CHECK_GOTO(rc = lys_compile_unres_glob(ctx, &unres), cleanup);
-
if (!(options & LY_CTX_EXPLICIT_COMPILE)) {
/* compile now */
LY_CHECK_GOTO(rc = ly_ctx_compile(ctx), cleanup);
@@ -299,7 +304,7 @@
cleanup:
ly_in_free(in, 0);
- lys_compile_unres_glob_erase(ctx, &unres, 0);
+ lys_unres_glob_erase(&unres);
if (rc) {
ly_ctx_destroy(ctx);
} else {
@@ -500,40 +505,25 @@
ly_ctx_compile(struct ly_ctx *ctx)
{
LY_ERR ret = LY_SUCCESS;
- struct lys_module *mod;
uint32_t i;
struct lys_glob_unres unres = {0};
LY_CHECK_ARG_RET(NULL, ctx, LY_EINVAL);
- /* (re)compile all the implemented modules */
- for (i = 0; i < ctx->list.count; ++i) {
- mod = ctx->list.objs[i];
- if (!mod->to_compile) {
- /* skip */
- continue;
- }
- assert(mod->implemented);
+ /* create dep sets and mark all the modules that will be (re)compiled */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_to_compile(ctx, &unres.dep_sets, NULL), cleanup);
- /* free the compiled module, if any */
- lysc_module_free(mod->compiled);
- mod->compiled = NULL;
-
- /* (re)compile the module */
- LY_CHECK_GOTO(ret = lys_compile(mod, &unres), cleanup);
- }
-
- /* resolve global unres */
- LY_CHECK_GOTO(ret = lys_compile_unres_glob(ctx, &unres), cleanup);
-
- /* success, unset all the flags */
- for (i = 0; i < ctx->list.count; ++i) {
- mod = ctx->list.objs[i];
- mod->to_compile = 0;
+ /* (re)compile all the dep sets */
+ for (i = 0; i < unres.dep_sets.count; ++i) {
+ LY_CHECK_GOTO(ret = lys_compile_dep_set_r(ctx, unres.dep_sets.objs[i], &unres), cleanup);
}
cleanup:
- lys_compile_unres_glob_erase(ctx, &unres, 0);
+ if (ret) {
+ /* revert changes of modules */
+ lys_unres_glob_revert(ctx, &unres);
+ }
+ lys_unres_glob_erase(&unres);
return ret;
}
diff --git a/src/schema_compile.c b/src/schema_compile.c
index 1e18c00..c2f120e 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -1197,17 +1197,29 @@
return LY_SUCCESS;
}
-LY_ERR
-lys_compile_unres_glob(struct ly_ctx *ctx, struct lys_glob_unres *unres)
+/**
+ * @brief Finish dependency set compilation by resolving all the unres sets.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] unres Global unres structure with the sets to resolve.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERECOMPILE if the dep set needs to be recompiled.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lys_compile_unres_depset(struct ly_ctx *ctx, struct lys_glob_unres *unres)
{
LY_ERR ret;
struct lysc_node *node;
struct lysc_type *type, *typeiter;
struct lysc_type_leafref *lref;
struct lysc_ctx cctx = {0};
+ struct lys_depset_unres *ds_unres;
LY_ARRAY_COUNT_TYPE v;
uint32_t i;
+ ds_unres = &unres->ds_unres;
+
/* fake compile context */
cctx.ctx = ctx;
cctx.path_len = 1;
@@ -1216,9 +1228,9 @@
/* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
* can be also leafref, in case it is already resolved, go through the chain and check that it does not
* point to the starting leafref type). The second round stores the first non-leafref type for later data validation. */
- for (i = 0; i < unres->leafrefs.count; ++i) {
+ for (i = 0; i < ds_unres->leafrefs.count; ++i) {
LY_ERR ret = LY_SUCCESS;
- node = unres->leafrefs.objs[i];
+ node = ds_unres->leafrefs.objs[i];
cctx.cur_mod = node->module;
cctx.pmod = node->module->parsed;
@@ -1243,8 +1255,8 @@
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_RET(ret);
}
- while (unres->leafrefs.count) {
- node = unres->leafrefs.objs[unres->leafrefs.count - 1];
+ while (ds_unres->leafrefs.count) {
+ node = ds_unres->leafrefs.objs[ds_unres->leafrefs.count - 1];
cctx.cur_mod = node->module;
cctx.pmod = node->module->parsed;
@@ -1269,32 +1281,30 @@
}
LOG_LOCBACK(1, 0, 0, 0);
- ly_set_rm_index(&unres->leafrefs, unres->leafrefs.count - 1, NULL);
+ ly_set_rm_index(&ds_unres->leafrefs, ds_unres->leafrefs.count - 1, NULL);
}
/* check xpath */
- while (unres->xpath.count) {
- node = unres->xpath.objs[unres->xpath.count - 1];
+ while (ds_unres->xpath.count) {
+ node = ds_unres->xpath.objs[ds_unres->xpath.count - 1];
cctx.cur_mod = node->module;
cctx.pmod = node->module->parsed;
LOG_LOCSET(node, NULL, NULL, NULL);
-
ret = lys_compile_unres_xpath(&cctx, node, unres);
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_RET(ret);
- ly_set_rm_index(&unres->xpath, unres->xpath.count - 1, NULL);
+ ly_set_rm_index(&ds_unres->xpath, ds_unres->xpath.count - 1, NULL);
}
/* finish incomplete default values compilation */
- while (unres->dflts.count) {
- struct lysc_unres_dflt *r = unres->dflts.objs[unres->dflts.count - 1];
+ while (ds_unres->dflts.count) {
+ struct lysc_unres_dflt *r = ds_unres->dflts.objs[ds_unres->dflts.count - 1];
cctx.cur_mod = r->leaf->module;
cctx.pmod = r->leaf->module->parsed;
LOG_LOCSET(&r->leaf->node, NULL, NULL, NULL);
-
if (r->leaf->nodetype == LYS_LEAF) {
ret = lys_compile_unres_leaf_dlft(&cctx, r->leaf, r->dflt, unres);
} else {
@@ -1304,17 +1314,17 @@
LY_CHECK_RET(ret);
lysc_unres_dflt_free(ctx, r);
- ly_set_rm_index(&unres->dflts, unres->dflts.count - 1, NULL);
+ ly_set_rm_index(&ds_unres->dflts, ds_unres->dflts.count - 1, NULL);
}
/* some unres items may have been added */
- if (unres->leafrefs.count || unres->xpath.count || unres->dflts.count) {
- return lys_compile_unres_glob(ctx, unres);
+ if (ds_unres->leafrefs.count || ds_unres->xpath.count || ds_unres->dflts.count) {
+ return lys_compile_unres_depset(ctx, unres);
}
/* finally, remove all disabled nodes */
- for (i = 0; i < unres->disabled.count; ++i) {
- node = unres->disabled.snodes[i];
+ for (i = 0; i < ds_unres->disabled.count; ++i) {
+ node = ds_unres->disabled.snodes[i];
if (node->flags & LYS_KEY) {
LOG_LOCSET(node, NULL, NULL, NULL);
LOGVAL(ctx, LYVE_REFERENCE, "Key \"%s\" is disabled by its if-features.", node->name);
@@ -1328,59 +1338,69 @@
return LY_SUCCESS;
}
-void
-lys_compile_unres_glob_revert(struct ly_ctx *ctx, struct lys_glob_unres *unres)
-{
- uint32_t i, prev_lo;
- struct lys_module *m;
- LY_ERR ret;
-
- for (i = 0; i < unres->implementing.count; ++i) {
- m = unres->implementing.objs[i];
- assert(m->implemented);
-
- /* make the module correctly non-implemented again */
- m->implemented = 0;
- lys_precompile_augments_deviations_revert(ctx, m);
- lysc_module_free(m->compiled);
- m->compiled = NULL;
- }
-
- for (i = 0; i < unres->creating.count; ++i) {
- m = unres->creating.objs[i];
-
- /* remove the module from the context and free it */
- ly_set_rm(&ctx->list, m, NULL);
- lys_module_free(m);
- }
-
- if (unres->implementing.count) {
- /* recompile previous context because some implemented modules are no longer implemented */
- prev_lo = ly_log_options(0);
- ret = lys_recompile(ctx);
- ly_log_options(prev_lo);
- if (ret) {
- LOGINT(ctx);
- }
- }
-}
-
-void
-lys_compile_unres_glob_erase(const struct ly_ctx *ctx, struct lys_glob_unres *unres, ly_bool recompiled)
+/**
+ * @brief Erase dep set unres.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] unres Global unres structure with the sets to resolve.
+ */
+static void
+lys_compile_unres_depset_erase(const struct ly_ctx *ctx, struct lys_glob_unres *unres)
{
uint32_t i;
- if (!recompiled) {
- ly_set_erase(&unres->implementing, NULL);
- ly_set_erase(&unres->creating, NULL);
+ for (i = 0; i < unres->ds_unres.dflts.count; ++i) {
+ lysc_unres_dflt_free(ctx, unres->ds_unres.dflts.objs[i]);
}
- for (i = 0; i < unres->dflts.count; ++i) {
- lysc_unres_dflt_free(ctx, unres->dflts.objs[i]);
+ ly_set_erase(&unres->ds_unres.dflts, NULL);
+ ly_set_erase(&unres->ds_unres.xpath, NULL);
+ ly_set_erase(&unres->ds_unres.leafrefs, NULL);
+ ly_set_erase(&unres->ds_unres.disabled, NULL);
+}
+
+LY_ERR
+lys_compile_dep_set_r(struct ly_ctx *ctx, struct ly_set *dep_set, struct lys_glob_unres *unres)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *mod;
+ uint32_t i;
+
+ for (i = 0; i < dep_set->count; ++i) {
+ mod = dep_set->objs[i];
+ if (!mod->to_compile) {
+ /* skip */
+ continue;
+ }
+ assert(mod->implemented);
+
+ /* free the compiled module, if any */
+ lysc_module_free(mod->compiled);
+ mod->compiled = NULL;
+
+ /* (re)compile the module */
+ LY_CHECK_GOTO(ret = lys_compile(mod, &unres->ds_unres), cleanup);
}
- ly_set_erase(&unres->dflts, NULL);
- ly_set_erase(&unres->xpath, NULL);
- ly_set_erase(&unres->leafrefs, NULL);
- ly_set_erase(&unres->disabled, NULL);
+
+ /* resolve dep set unres */
+ ret = lys_compile_unres_depset(ctx, unres);
+ if (ret == LY_ERECOMPILE) {
+ /* new module is implemented, discard current dep set unres and recompile the whole dep set */
+ lys_compile_unres_depset_erase(ctx, unres);
+ return lys_compile_dep_set_r(ctx, dep_set, unres);
+ } else if (ret) {
+ /* error */
+ goto cleanup;
+ }
+
+ /* success, unset the flags of all the modules in the dep set */
+ for (i = 0; i < dep_set->count; ++i) {
+ mod = dep_set->objs[i];
+ mod->to_compile = 0;
+ }
+
+cleanup:
+ lys_compile_unres_depset_erase(ctx, unres);
+ return ret;
}
/**
@@ -1474,7 +1494,7 @@
}
LY_ERR
-lys_compile(struct lys_module *mod, struct lys_glob_unres *unres)
+lys_compile(struct lys_module *mod, struct lys_depset_unres *unres)
{
struct lysc_ctx ctx = {0};
struct lysc_module *mod_c = NULL;
@@ -1487,7 +1507,7 @@
LY_CHECK_ARG_RET(NULL, mod, mod->parsed, !mod->compiled, mod->ctx, LY_EINVAL);
- assert(mod->implemented);
+ assert(mod->implemented && mod->to_compile);
sp = mod->parsed;
@@ -1641,7 +1661,7 @@
*
* @param[in] mod Module to examine.
* @return LY_SUCCESS on success.
- * @return LY_ERECOMPILE on required recompilation.
+ * @return LY_ERECOMPILE on required recompilation of the dep set.
* @return LY_ERR on error.
*/
static LY_ERR
@@ -1658,6 +1678,7 @@
if (!m->to_compile) {
/* module was not/will not be compiled in this compilation (so disabled nodes are not present) */
+ m->to_compile = 1;
return LY_ERECOMPILE;
}
diff --git a/src/schema_compile.h b/src/schema_compile.h
index a0cdadf..1fd2338 100644
--- a/src/schema_compile.h
+++ b/src/schema_compile.h
@@ -52,7 +52,7 @@
struct ly_set devs; /**< set of compiled non-applied deviations (stored ::lysc_deviation *) */
struct ly_set uses_augs; /**< set of compiled non-applied uses augments (stored ::lysc_augment *) */
struct ly_set uses_rfns; /**< set of compiled non-applied uses refines (stored ::lysc_refine *) */
- struct lys_glob_unres *unres; /**< global unres sets */
+ struct lys_depset_unres *unres; /**< dependency set unres sets */
uint32_t path_len; /**< number of path bytes used */
uint32_t compile_opts; /**< various @ref scflags. */
#define LYSC_CTX_BUFSIZE 4078
@@ -60,14 +60,10 @@
};
/**
- * @brief Structure for unresolved items that may depend on any implemented module data so their resolution
- * can only be performed after all module basic compilation is done.
+ * @brief Structure for unresolved items that may depend on any implemented module data in the dependency set
+ * so their resolution can only be performed after the whole dep set compilation is done.
*/
-struct lys_glob_unres {
- struct ly_set implementing; /**< set of YANG schemas being atomically implemented (compiled); the first added
- module is always the explcitly implemented module, the other ones are dependencies */
- struct ly_set creating; /**< set of YANG schemas being atomically created (parsed); it is a subset of implemented
- and all these modules are freed if any error occurs */
+struct lys_depset_unres {
struct ly_set xpath; /**< when/must to check */
struct ly_set leafrefs; /**< to validate leafref's targets */
struct ly_set dflts; /**< set of incomplete default values */
@@ -75,6 +71,18 @@
};
/**
+ * @brief Unres structure global for compilation.
+ */
+struct lys_glob_unres {
+ struct ly_set dep_sets; /**< set of dependency sets of modules, see ::ly_ctx_compile_deps_mod_r() */
+ struct ly_set implementing; /**< set of YANG schemas being atomically implemented (compiled); the first added
+ module is always the explcitly implemented module, the other ones are dependencies */
+ struct ly_set creating; /**< set of YANG schemas being atomically created (parsed); it is a subset of implemented
+ and all these modules are freed if any error occurs */
+ struct lys_depset_unres ds_unres; /**< unres specific for the current dependency set */
+};
+
+/**
* @brief Structure for remembering default values of leaves and leaf-lists. They are resolved at schema compilation
* end when the whole schema tree is available.
*/
@@ -249,54 +257,14 @@
void *prefix_data, ly_bool implement, struct lys_glob_unres *unres, const struct lys_module **mod_p);
/**
- * @brief Finish compilation of all the global unres sets.
- * Will always finish the compilation (never return @p unres with `recompile` set).
+ * @brief Compile all flagged modules in a dependency set, recursively if recompilation is needed.
*
* @param[in] ctx libyang context.
- * @param[in] unres Global unres structure with the sets to resolve.
+ * @param[in] dep_set Dependency set to compile.
+ * @param[in,out] unres Global unres to use.
* @return LY_ERR value.
*/
-LY_ERR lys_compile_unres_glob(struct ly_ctx *ctx, struct lys_glob_unres *unres);
-
-/**
- * @brief Revert a failed compilation (free new modules, unimplement newly implemented modules).
- *
- * @param[in] ctx libyang context.
- * @param[in] unres Global unres set with newly implemented modules.
- */
-void lys_compile_unres_glob_revert(struct ly_ctx *ctx, struct lys_glob_unres *unres);
-
-/**
- * @brief Erase the global unres.
- *
- * @param[in] ctx libyang context.
- * @param[in] unres Global unres structure with the sets.
- * @param[in] recompiled Whether to keep the set of new parsed and implemented modules.
- */
-void lys_compile_unres_glob_erase(const struct ly_ctx *ctx, struct lys_glob_unres *unres, ly_bool recompiled);
-
-/**
- * @brief Compile schema into a validated schema linking all the references.
- *
- * Implemented flag of @p mod must be set meaning this function should be called only if the module
- * is being recompiled, otherwise call ::lys_implement().
- *
- * @param[in] mod Pointer to the schema structure holding pointers to both schema structure types. The ::lys_module#parsed
- * member is used as input and ::lys_module#compiled is used to hold the result of the compilation.
- * @param[in,out] unres Global unres structure to add to.
- * @return LY_SUCCESS on success.
- * @return LY_ERR on error.
- */
-LY_ERR lys_compile(struct lys_module *mod, struct lys_glob_unres *unres);
-
-/**
- * @brief Recompile the whole context based on the current flags.
- *
- * @param[in] ctx Context to recompile.
- * @return LY_SUCCESS on success.
- * @return LY_ERR on error.
- */
-LY_ERR lys_recompile(struct ly_ctx *ctx);
+LY_ERR lys_compile_dep_set_r(struct ly_ctx *ctx, struct ly_set *dep_set, struct lys_glob_unres *unres);
/**
* @brief Implement a single module. Does not actually compile, only marks to_compile!
diff --git a/src/schema_compile_amend.h b/src/schema_compile_amend.h
index 0ad49b4..4d368dd 100644
--- a/src/schema_compile_amend.h
+++ b/src/schema_compile_amend.h
@@ -162,7 +162,7 @@
* @param[in] mod Module to process.
* @param[in,out] unres Global unres to use.
* @return LY_SUCCESS on success.
- * @return LY_ERECOMPILE on required recompilation.
+ * @return LY_ERECOMPILE on required recompilation of the dep set.
* @return LY_ERR on error.
*/
LY_ERR lys_precompile_augments_deviations(struct lys_module *mod, struct lys_glob_unres *unres);
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 01efc11..18096a6 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -38,6 +38,7 @@
#include "parser_schema.h"
#include "path.h"
#include "schema_compile.h"
+#include "schema_compile_amend.h"
#include "schema_features.h"
#include "set.h"
#include "tree.h"
@@ -831,21 +832,249 @@
r = lys_implement(mod, NULL, unres);
LY_CHECK_ERR_GOTO(r && (r != LY_ERECOMPILE), ret = r, cleanup);
-
- lys_compile_unres_glob_erase(mod->ctx, unres, 1);
- } else if (ret) {
- goto cleanup;
- }
}
}
- /* resolve unres */
- LY_CHECK_GOTO(ret = lys_compile_unres_glob(mod->ctx, unres), cleanup);
-
cleanup:
return ret;
}
+/**
+ * @brief Check whether it may be needed to (re)compile a module from a particular dependency set.
+ *
+ * Dependency set includes all modules that need to be (re)compiled in case any of the modules
+ * is (re)compiled. The reason is possible disabled nodes and updaing leafref targets to point
+ * to the newly compiled modules. Using the import relation, the dependency is reflexive
+ * because of possible foreign augments and deviations, which are compiled during the target
+ * module compilation.
+ *
+ * @param[in] mod Module to process.
+ * @param[in,out] ctx_set Set with all not-yet-processed modules.
+ * @param[in,out] dep_set Current dependency set to update.
+ * @param[out] nothing_to_compile Set if there is nothing to compile in the module.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_unres_dep_sets_mod_r(struct lys_module *mod, struct ly_set *ctx_set, struct ly_set *dep_set, ly_bool *nothing_to_compile)
+{
+ struct lys_module *mod2;
+ struct lysp_import *imports;
+ uint32_t i;
+ LY_ARRAY_COUNT_TYPE u, v;
+ ly_bool found;
+
+ if (nothing_to_compile) {
+ *nothing_to_compile = 0;
+ }
+
+ if (!ly_set_contains(ctx_set, mod, &i)) {
+ /* it was already processed */
+ return LY_SUCCESS;
+ }
+
+ /* remove it from the set, we are processing it now */
+ ly_set_rm_index(ctx_set, i, NULL);
+
+ if (mod->to_compile && mod->compiled && !lys_has_recompiled(mod)) {
+ /* it would be recompiled but there are no statements requiring recompilation */
+ mod->to_compile = 0;
+ return LY_SUCCESS;
+ }
+
+ /* add a new dependent module into the dep set */
+ LY_CHECK_RET(ly_set_add(dep_set, mod, 1, NULL));
+
+ if (!lys_has_compiled(mod)) {
+ /* nothing to compile */
+ if (nothing_to_compile) {
+ *nothing_to_compile = 1;
+ }
+ return LY_SUCCESS;
+ }
+
+ /* process imports of the module and submodules */
+ imports = mod->parsed->imports;
+ LY_ARRAY_FOR(imports, u) {
+ LY_CHECK_RET(lys_unres_dep_sets_mod_r(imports[u].module, ctx_set, dep_set, NULL));
+ }
+ LY_ARRAY_FOR(mod->parsed->includes, v) {
+ imports = mod->parsed->includes[v].submodule->imports;
+ LY_ARRAY_FOR(imports, u) {
+ LY_CHECK_RET(lys_unres_dep_sets_mod_r(imports[u].module, ctx_set, dep_set, NULL));
+ }
+ }
+
+ /* process modules and submodules importing this module */
+ for (i = 0; i < ctx_set->count; ++i) {
+ mod2 = ctx_set->objs[i];
+ found = 0;
+
+ imports = mod2->parsed->imports;
+ LY_ARRAY_FOR(imports, u) {
+ if (imports[u].module == mod) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ LY_ARRAY_FOR(mod2->parsed->includes, v) {
+ imports = mod2->parsed->includes[v].submodule->imports;
+ LY_ARRAY_FOR(imports, u) {
+ if (imports[u].module == mod) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ break;
+ }
+ }
+ }
+
+ if (found) {
+ LY_CHECK_RET(lys_unres_dep_sets_mod_r(mod2, ctx_set, dep_set, NULL));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_unres_dep_sets_to_compile(struct ly_ctx *ctx, struct ly_set *main_set, struct lys_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lys_module *m;
+ struct ly_set *dep_set = NULL, *ctx_set = NULL;
+ uint32_t i;
+ ly_bool nothing_to_compile;
+
+ /* start with a duplicate set of modules that we will remove from */
+ LY_CHECK_GOTO(ret = ly_set_dup(&ctx->list, NULL, &ctx_set), cleanup);
+
+ while (ctx_set->count) {
+ /* create new dep set */
+ LY_CHECK_GOTO(ret = ly_set_new(&dep_set), cleanup);
+
+ if (mod) {
+ /* use the module create a dep set with the rest of its dependent modules */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_mod_r(mod, ctx_set, dep_set, ¬hing_to_compile), cleanup);
+ } else {
+ /* use first ctx mod to create a dep set with the rest of its dependent modules */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_mod_r(ctx_set->objs[0], ctx_set, dep_set, ¬hing_to_compile), cleanup);
+ }
+
+ /* check whether there is any module that will be (re)compiled */
+ for (i = 0; i < dep_set->count; ++i) {
+ m = dep_set->objs[i];
+ if (m->to_compile) {
+ break;
+ }
+ }
+
+ if (i < dep_set->count) {
+ /* if there is, all the implemented modules need to be recompiled */
+ for (i = 0; i < dep_set->count; ++i) {
+ m = dep_set->objs[i];
+ if (m->implemented) {
+ m->to_compile = 1;
+ }
+ }
+ }
+
+ if (dep_set->count) {
+ /* add the dep set into main set */
+ LY_CHECK_GOTO(ret = ly_set_add(main_set, dep_set, 1, NULL), cleanup);
+ } else {
+ ly_set_free(dep_set, NULL);
+ }
+ dep_set = NULL;
+
+ if (mod) {
+ /* we need dep set only for this module */
+ break;
+ }
+ }
+
+cleanup:
+ ly_set_free(dep_set, NULL);
+ ly_set_free(ctx_set, NULL);
+ return ret;
+}
+
+void
+lys_unres_glob_revert(struct ly_ctx *ctx, struct lys_glob_unres *unres)
+{
+ uint32_t i, j, idx, prev_lo;
+ struct ly_set *dep_set;
+ struct lys_module *m;
+ LY_ERR ret;
+
+ for (i = 0; i < unres->implementing.count; ++i) {
+ m = unres->implementing.objs[i];
+ assert(m->implemented);
+
+ /* make the module correctly non-implemented again */
+ m->implemented = 0;
+ lys_precompile_augments_deviations_revert(ctx, m);
+ lysc_module_free(m->compiled);
+ m->compiled = NULL;
+
+ /* should not be made implemented */
+ m->to_compile = 0;
+ }
+
+ for (i = 0; i < unres->creating.count; ++i) {
+ m = unres->creating.objs[i];
+
+ /* remove the module from the context and free it */
+ ly_set_rm(&ctx->list, m, NULL);
+ lys_module_free(m);
+
+ if (unres->dep_sets.count) {
+ /* remove it also from dep sets, if created */
+ for (j = 0; j < unres->dep_sets.count; ++j) {
+ dep_set = unres->dep_sets.objs[j];
+ if (ly_set_contains(dep_set, m, &idx)) {
+ ly_set_rm_index(dep_set, idx, NULL);
+ break;
+ }
+ }
+ assert(j < unres->dep_sets.count);
+ }
+ }
+
+ if (unres->implementing.count) {
+ /* recompile previous context because some implemented modules are no longer implemented,
+ * we can reuse the current to_compile flags */
+ prev_lo = ly_log_options(0);
+ ret = ly_ctx_compile(ctx);
+ ly_log_options(prev_lo);
+ if (ret) {
+ LOGINT(ctx);
+ }
+ }
+}
+
+void
+lys_unres_glob_erase(struct lys_glob_unres *unres)
+{
+ uint32_t i;
+
+ for (i = 0; i < unres->dep_sets.count; ++i) {
+ ly_set_free(unres->dep_sets.objs[i], NULL);
+ }
+ ly_set_erase(&unres->dep_sets, NULL);
+ ly_set_erase(&unres->implementing, NULL);
+ ly_set_erase(&unres->creating, NULL);
+
+ assert(!unres->ds_unres.xpath.count);
+ assert(!unres->ds_unres.leafrefs.count);
+ assert(!unres->ds_unres.dflts.count);
+ assert(!unres->ds_unres.disabled.count);
+}
+
API LY_ERR
lys_set_implemented(struct lys_module *mod, const char **features)
{
@@ -858,11 +1087,19 @@
ret = _lys_set_implemented(mod, features, &unres);
LY_CHECK_GOTO(ret, cleanup);
+ if (!(mod->ctx->flags & LY_CTX_EXPLICIT_COMPILE)) {
+ /* create dep set for the module and mark all the modules that will be (re)compiled */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_to_compile(mod->ctx, &unres.dep_sets, mod), cleanup);
+
+ /* (re)compile the whole dep set */
+ LY_CHECK_GOTO(ret = lys_compile_dep_set_r(mod->ctx, unres.dep_sets.objs[0], &unres), cleanup);
+ }
+
cleanup:
if (ret) {
- lys_compile_unres_glob_revert(mod->ctx, &unres);
+ lys_unres_glob_revert(mod->ctx, &unres);
}
- lys_compile_unres_glob_erase(mod->ctx, &unres, 0);
+ lys_unres_glob_erase(&unres);
return ret;
}
@@ -1433,13 +1670,21 @@
ret = _lys_set_implemented(mod, features, &unres);
LY_CHECK_GOTO(ret, cleanup);
+ if (!(ctx->flags & LY_CTX_EXPLICIT_COMPILE)) {
+ /* create dep set for the module and mark all the modules that will be (re)compiled */
+ LY_CHECK_GOTO(ret = lys_unres_dep_sets_to_compile(ctx, &unres.dep_sets, mod), cleanup);
+
+ /* (re)compile the whole dep set */
+ LY_CHECK_GOTO(ret = lys_compile_dep_set_r(ctx, unres.dep_sets.objs[0], &unres), cleanup);
+ }
+
cleanup:
if (ret) {
- lys_compile_unres_glob_revert(ctx, &unres);
+ lys_unres_glob_revert(ctx, &unres);
} else if (module) {
*module = mod;
}
- lys_compile_unres_glob_erase(ctx, &unres, 0);
+ lys_unres_glob_erase(&unres);
return ret;
}
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 242dc8e..82a5610 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -1935,3 +1935,39 @@
return parent;
}
+
+ly_bool
+lys_has_recompiled(const struct lys_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (LYSP_HAS_RECOMPILED(mod->parsed)) {
+ return 1;
+ }
+
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ if (LYSP_HAS_RECOMPILED(mod->parsed->includes[u].submodule)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+ly_bool
+lys_has_compiled(const struct lys_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (LYSP_HAS_COMPILED(mod->parsed)) {
+ return 1;
+ }
+
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ if (LYSP_HAS_COMPILED(mod->parsed->includes[u].submodule)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index edd0cc6..db276a0 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -534,6 +534,31 @@
*/
LY_ERR _lys_set_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres);
+/**
+ * @brief Create dependency sets for all modules in a context.
+ *
+ * @param[in] ctx Context to use.
+ * @param[in,out] main_set Set of dependency module sets.
+ * @param[in] mod Optional only module whose dependency set is needed, otherwise all sets are created.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_unres_dep_sets_to_compile(struct ly_ctx *ctx, struct ly_set *main_set, struct lys_module *mod);
+
+/**
+ * @brief Revert changes stored in global compile context after a failed compilation.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] unres Global unres to use.
+ */
+void lys_unres_glob_revert(struct ly_ctx *ctx, struct lys_glob_unres *unres);
+
+/**
+ * @brief Erase the global compile context.
+ *
+ * @param[in] unres Global unres to erase.
+ */
+void lys_unres_glob_erase(struct lys_glob_unres *unres);
+
typedef LY_ERR (*lys_custom_check)(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod,
void *check_data);
@@ -817,4 +842,38 @@
const struct lys_module *ly_resolve_prefix(const struct ly_ctx *ctx, const void *prefix, size_t prefix_len,
LY_VALUE_FORMAT format, const void *prefix_data);
+/**
+ * @brief Learn whether @p PMOD needs to be recompiled if it is implemented.
+ *
+ * @param[in] PMOD Parsed module or submodule.
+ * @return Whether it has statements that are recompiled or not.
+ */
+#define LYSP_HAS_RECOMPILED(PMOD) \
+ (PMOD->data || PMOD->rpcs || PMOD->notifs || PMOD->exts)
+
+/**
+ * @brief Learn whether the module has statements that need to be recompiled or not.
+ *
+ * @param[in] mod Module to examine.
+ * @return Whether it has statements that are recompiled or not.
+ */
+ly_bool lys_has_recompiled(const struct lys_module *mod);
+
+/**
+ * @brief Learn whether @p PMOD needs to be compiled if it is implemented.
+ *
+ * @param[in] PMOD Parsed module or submodule.
+ * @return Whether it needs (has) a compiled module or not.
+ */
+#define LYSP_HAS_COMPILED(PMOD) \
+ (LYSP_HAS_RECOMPILED(PMOD) || PMOD->identities || PMOD->augments || PMOD->deviations)
+
+/**
+ * @brief Learn whether the module has statements that need to be compiled or not.
+ *
+ * @param[in] mod Module to examine.
+ * @return Whether it needs compiled module or not.
+ */
+ly_bool lys_has_compiled(const struct lys_module *mod);
+
#endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/tests/utests/basic/test_context.c b/tests/utests/basic/test_context.c
index ff755ad..4d392ed 100644
--- a/tests/utests/basic/test_context.c
+++ b/tests/utests/basic/test_context.c
@@ -216,7 +216,7 @@
assert_int_equal(LY_SUCCESS, ly_in_new_memory("module x {namespace urn:x;prefix x;}", &in));
assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, 4, NULL, NULL, &unres.creating, &mod1));
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
CHECK_LOG_CTX("Invalid schema input format.", NULL);
@@ -235,7 +235,7 @@
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-30;}");
assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;include y;}", &in));
assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", "Line number 1.");
@@ -244,15 +244,15 @@
ly_in_free(in, 0);
assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;}", &in));
assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", "Line number 1.");
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to b {prefix b;}}");
assert_int_equal(LY_SUCCESS, ly_in_new_memory("module b {namespace urn:b;prefix b;include y;}", &in));
assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
- lys_compile_unres_glob_revert(UTEST_LYCTX, &unres);
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_revert(UTEST_LYCTX, &unres);
+ lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
CHECK_LOG_CTX("Including \"y\" submodule into \"b\" failed.", NULL,
"Name collision between submodules of name \"y\".", "Line number 1.");
@@ -262,7 +262,7 @@
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-31;}");
assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y; revision 2018-10-31;}", &in));
assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2));
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
assert_string_equal("2018-10-31", mod2->parsed->includes[0].submodule->revs[0].date);
@@ -387,15 +387,15 @@
assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2));
assert_int_equal(LY_EDENIED, lys_implement(mod2, NULL, &unres));
CHECK_LOG_CTX("Module \"a@2018-10-24\" is present in the context in other implemented revision (2018-10-23).", NULL);
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
ly_in_reset(in1);
/* it is already there, fine */
assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL));
/* insert the second module only as imported, not implemented */
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
ly_in_reset(in2);
assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2));
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
assert_non_null(mod2);
assert_ptr_not_equal(mod, mod2);
mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a");
@@ -404,7 +404,7 @@
assert_ptr_equal(mod, mod2);
/* work with module with no revision */
assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in0, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod));
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
assert_ptr_equal(mod, ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
assert_ptr_not_equal(mod, ly_ctx_get_module_latest(UTEST_LYCTX, "a"));
@@ -413,7 +413,7 @@
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1));
assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod));
CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL);
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
while ((mod = (struct lys_module *)ly_ctx_get_module_iter(UTEST_LYCTX, &index))) {
assert_string_equal(names[index - 1], mod->name);
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index efbff1b..b635fb4 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -78,7 +78,7 @@
"feature f1;feature f2 {if-feature f1;}}";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod));
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
assert_int_equal(0, mod->implemented);
assert_int_equal(LY_SUCCESS, lys_set_implemented(mod, NULL));
@@ -102,7 +102,7 @@
str = "submodule test {belongs-to xxx {prefix x;}}";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL));
- lys_compile_unres_glob_erase(UTEST_LYCTX, &unres, 0);
+ lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL);