mirror of
https://github.com/ganelson/inform.git
synced 2024-07-08 01:54:21 +03:00
585 lines
21 KiB
OpenEdge ABL
585 lines
21 KiB
OpenEdge ABL
[Kits::] Kit Services.
|
|
|
|
Behaviour specific to copies of the kit genre.
|
|
|
|
@h Scanning metadata.
|
|
Metadata for kits is stored in the following structure. "Attachment" for a
|
|
kit is the process of taking the Inter code from a binary Inter file in the
|
|
kit directory and merging it into code already generated by the |core|
|
|
module of |inform7|.
|
|
|
|
=
|
|
typedef struct inform_kit {
|
|
struct inbuild_copy *as_copy;
|
|
|
|
struct text_stream *attachment_point; /* where in the Inter hierarchy to attach this */
|
|
int priority; /* lower kits are attached before higher ones */
|
|
|
|
struct text_stream *early_source; /* additional source text to spool in */
|
|
struct linked_list *ittt; /* of |inform_kit_ittt| */
|
|
struct linked_list *kind_definitions; /* of |text_stream| */
|
|
struct linked_list *extensions; /* of |inbuild_requirement| */
|
|
struct linked_list *activations; /* of |element_activation| */
|
|
struct linked_list *configurations; /* of |kit_configuration| */
|
|
struct text_stream *index_structure; /* for indexing projects using this kit */
|
|
int defines_Main; /* does the Inter code in this kit define the |Main| routine? */
|
|
int supports_nl; /* does the Inter code in this kit support a natural language extension? */
|
|
CLASS_DEFINITION
|
|
} inform_kit;
|
|
|
|
@ Kits come with an "if this then that" service for including other kits,
|
|
and we represent rules with the following:
|
|
|
|
=
|
|
typedef struct inform_kit_ittt {
|
|
struct text_stream *if_name;
|
|
int if_included;
|
|
struct text_stream *then_name;
|
|
CLASS_DEFINITION
|
|
} inform_kit_ittt;
|
|
|
|
@ Kits can also enable elements of the Inform programming language: that is,
|
|
enable compiler support for them. For example, the WorldModelKit enables
|
|
interactive fiction features of the compiler, but BasicInformKit does not.
|
|
|
|
=
|
|
typedef struct element_activation {
|
|
struct text_stream *element_name;
|
|
int activate;
|
|
CLASS_DEFINITION
|
|
} element_activation;
|
|
|
|
@ And kits can be configured with constants linked into them: for example,
|
|
the constant |BasicInformKit`AMERICAN_DIALECT_CFGF|. These are mostly set by
|
|
use options.
|
|
|
|
=
|
|
typedef struct kit_configuration {
|
|
struct inform_kit *owner;
|
|
struct text_stream *symbol_name;
|
|
int is_flag;
|
|
CLASS_DEFINITION
|
|
} kit_configuration;
|
|
|
|
@ Here goes, then:
|
|
|
|
=
|
|
void Kits::scan(inbuild_copy *C) {
|
|
inform_kit *K = CREATE(inform_kit);
|
|
K->as_copy = C;
|
|
if (C == NULL) internal_error("no copy to scan");
|
|
Copies::set_metadata(C, STORE_POINTER_inform_kit(K));
|
|
|
|
K->attachment_point = Str::new();
|
|
WRITE_TO(K->attachment_point, "/main/%S", C->edition->work->title);
|
|
K->priority = 10;
|
|
|
|
K->early_source = NULL;
|
|
K->ittt = NEW_LINKED_LIST(inform_kit_ittt);
|
|
K->kind_definitions = NEW_LINKED_LIST(text_stream);
|
|
K->extensions = NEW_LINKED_LIST(inbuild_requirement);
|
|
K->activations = NEW_LINKED_LIST(element_activation);
|
|
K->configurations = NEW_LINKED_LIST(kit_configuration);
|
|
K->index_structure = NULL;
|
|
K->defines_Main = FALSE;
|
|
K->supports_nl = FALSE;
|
|
|
|
filename *F = Filenames::in(C->location_if_path, I"kit_metadata.json");
|
|
if (TextFiles::exists(F) == FALSE)
|
|
SVEXPLAIN(2, "(no JSON metadata file found at %f)\n", F);
|
|
JSONMetadata::read_metadata_file(C, F, NULL, NULL);
|
|
|
|
if (C->metadata_record) {
|
|
@<Extract activations@>;
|
|
JSON_value *kit_details =
|
|
JSON::look_up_object(C->metadata_record, I"kit-details");
|
|
if (kit_details) @<Extract the kit details@>;
|
|
JSON_value *needs = JSON::look_up_object(C->metadata_record, I"needs");
|
|
if (needs) {
|
|
JSON_value *E;
|
|
LOOP_OVER_LINKED_LIST(E, JSON_value, needs->if_list)
|
|
@<Extract this possibly conditional requirement@>;
|
|
}
|
|
}
|
|
}
|
|
|
|
@<Extract activations@> =
|
|
JSON_value *activates = JSON::look_up_object(C->metadata_record, I"activates");
|
|
if (activates) {
|
|
JSON_value *E;
|
|
LOOP_OVER_LINKED_LIST(E, JSON_value, activates->if_list)
|
|
Kits::activation(K, E->if_string, TRUE);
|
|
}
|
|
JSON_value *deactivates = JSON::look_up_object(C->metadata_record, I"deactivates");
|
|
if (deactivates) {
|
|
JSON_value *E;
|
|
LOOP_OVER_LINKED_LIST(E, JSON_value, deactivates->if_list)
|
|
Kits::activation(K, E->if_string, FALSE);
|
|
}
|
|
|
|
@<Extract the kit details@> =
|
|
JSON_value *has_priority = JSON::look_up_object(kit_details, I"has-priority");
|
|
if (has_priority) K->priority = has_priority->if_integer;
|
|
JSON_value *defines_Main = JSON::look_up_object(kit_details, I"defines-Main");
|
|
if (defines_Main) K->defines_Main = defines_Main->if_boolean;
|
|
JSON_value *is_language_kit = JSON::look_up_object(kit_details, I"is-language-kit");
|
|
if (is_language_kit) K->supports_nl = is_language_kit->if_boolean;
|
|
JSON_value *indexes_with_structure =
|
|
JSON::look_up_object(kit_details, I"indexes-with-structure");
|
|
if (indexes_with_structure) K->index_structure = indexes_with_structure->if_string;
|
|
JSON_value *provides_kinds = JSON::look_up_object(kit_details, I"provides-kinds");
|
|
if (provides_kinds) {
|
|
JSON_value *E;
|
|
LOOP_OVER_LINKED_LIST(E, JSON_value, provides_kinds->if_list)
|
|
ADD_TO_LINKED_LIST(E->if_string, text_stream, K->kind_definitions);
|
|
}
|
|
JSON_value *inserts_source_text = JSON::look_up_object(kit_details, I"inserts-source-text");
|
|
if (inserts_source_text) {
|
|
K->early_source = Str::duplicate(inserts_source_text->if_string);
|
|
WRITE_TO(K->early_source, "\n\n");
|
|
}
|
|
JSON_value *configs = JSON::look_up_object(kit_details, I"configuration-flags");
|
|
if (configs) {
|
|
int f = TRUE;
|
|
@<Extract the configuration symbols@>;
|
|
}
|
|
configs = JSON::look_up_object(kit_details, I"configuration-values");
|
|
if (configs) {
|
|
int f = FALSE;
|
|
@<Extract the configuration symbols@>;
|
|
}
|
|
|
|
@<Extract this possibly conditional requirement@> =
|
|
int parity = TRUE;
|
|
JSON_value *if_clause = JSON::look_up_object(E, I"if");
|
|
JSON_value *unless_clause = JSON::look_up_object(E, I"unless");
|
|
if (unless_clause) {
|
|
if_clause = unless_clause; parity = FALSE;
|
|
}
|
|
JSON_value *need_clause = JSON::look_up_object(E, I"need");
|
|
if (need_clause) {
|
|
JSON_value *need_type = JSON::look_up_object(need_clause, I"type");
|
|
JSON_value *need_title = JSON::look_up_object(need_clause, I"title");
|
|
JSON_value *need_author = JSON::look_up_object(need_clause, I"author");
|
|
JSON_value *need_version = JSON::look_up_object(need_clause, I"version");
|
|
if (Str::eq(need_type->if_string, I"extension"))
|
|
@<Deal with an extension dependency@>
|
|
else if (Str::eq(need_type->if_string, I"kit"))
|
|
@<Deal with a kit dependency@>
|
|
else {
|
|
TEMPORARY_TEXT(err)
|
|
WRITE_TO(err, "a kit can only have extensions and kits as dependencies");
|
|
Copies::attach_error(C, CopyErrors::new_T(METADATA_MALFORMED_CE, -1, err));
|
|
DISCARD_TEXT(err)
|
|
}
|
|
}
|
|
|
|
@<Deal with an extension dependency@> =
|
|
if (if_clause) {
|
|
TEMPORARY_TEXT(err)
|
|
WRITE_TO(err, "a kit can only have an extension as a dependency unconditionally");
|
|
Copies::attach_error(C, CopyErrors::new_T(METADATA_MALFORMED_CE, -1, err));
|
|
DISCARD_TEXT(err)
|
|
}
|
|
text_stream *extension_title = need_title->if_string;
|
|
text_stream *extension_author = need_author?(need_author->if_string):NULL;
|
|
inbuild_work *work = Works::new(extension_genre, extension_title, extension_author);
|
|
if (need_version) @<Add versioned extension@>
|
|
else @<Add unversioned extension@>;
|
|
|
|
@<Add versioned extension@> =
|
|
semantic_version_number V = VersionNumbers::from_text(need_version->if_string);
|
|
if (VersionNumbers::is_null(V)) {
|
|
TEMPORARY_TEXT(err)
|
|
WRITE_TO(err, "cannot read version number '%S'", need_version->if_string);
|
|
Copies::attach_error(C, CopyErrors::new_T(METADATA_MALFORMED_CE, -1, err));
|
|
DISCARD_TEXT(err)
|
|
} else {
|
|
inbuild_requirement *req = Requirements::new(work,
|
|
VersionNumberRanges::compatibility_range(V));
|
|
ADD_TO_LINKED_LIST(req, inbuild_requirement, K->extensions);
|
|
}
|
|
|
|
@<Add unversioned extension@> =
|
|
inbuild_requirement *req = Requirements::any_version_of(work);
|
|
ADD_TO_LINKED_LIST(req, inbuild_requirement, K->extensions);
|
|
|
|
@<Deal with a kit dependency@> =
|
|
text_stream *if_kit = C->edition->work->title;
|
|
if (if_clause) {
|
|
JSON_value *if_type = JSON::look_up_object(if_clause, I"type");
|
|
if (Str::eq(if_type->if_string, I"kit") == FALSE) {
|
|
TEMPORARY_TEXT(err)
|
|
WRITE_TO(err, "a kit dependency can only be conditional on other kits");
|
|
Copies::attach_error(C, CopyErrors::new_T(METADATA_MALFORMED_CE, -1, err));
|
|
DISCARD_TEXT(err)
|
|
} else {
|
|
JSON_value *if_title = JSON::look_up_object(if_clause, I"title");
|
|
if (if_title) if_kit = if_title->if_string; /* a line for IF fans */
|
|
}
|
|
}
|
|
Kits::dependency(K, if_kit, parity, need_title->if_string);
|
|
|
|
@<Extract the configuration symbols@> =
|
|
JSON_value *E;
|
|
LOOP_OVER_LINKED_LIST(E, JSON_value, configs->if_list) {
|
|
kit_configuration *kc = CREATE(kit_configuration);
|
|
kc->owner = K;
|
|
kc->symbol_name = Str::duplicate(E->if_string);
|
|
kc->is_flag = f;
|
|
ADD_TO_LINKED_LIST(kc, kit_configuration, K->configurations);
|
|
}
|
|
|
|
@ We provide if this then that, where |inc| is true, and if this then not that,
|
|
where it's false.
|
|
|
|
=
|
|
void Kits::dependency(inform_kit *K, text_stream *if_text, int inc, text_stream *then_text) {
|
|
inform_kit_ittt *ITTT = CREATE(inform_kit_ittt);
|
|
ITTT->if_name = Str::duplicate(if_text);
|
|
ITTT->if_included = inc;
|
|
ITTT->then_name = Str::duplicate(then_text);
|
|
ADD_TO_LINKED_LIST(ITTT, inform_kit_ittt, K->ittt);
|
|
}
|
|
|
|
@ Language elements can similarly be activated or deactivated, though the
|
|
latter may not be useful in practice:
|
|
|
|
=
|
|
void Kits::activation(inform_kit *K, text_stream *name, int act) {
|
|
element_activation *EA = CREATE(element_activation);
|
|
EA->element_name = Str::duplicate(name);
|
|
EA->activate = act;
|
|
ADD_TO_LINKED_LIST(EA, element_activation, K->activations);
|
|
}
|
|
|
|
@h The kits included by a project.
|
|
A project can call this to obtain the |inform_kit| structure for the copy of
|
|
a kit, going only on a name such as |BasicInformKit|:
|
|
|
|
=
|
|
inform_kit *Kits::find_by_name(text_stream *name, linked_list *nest_list,
|
|
inbuild_requirement *req) {
|
|
if (req == NULL) req = Requirements::any_version_of(Works::new(kit_genre, name, I""));
|
|
inbuild_search_result *R = Nests::search_for_best(req, nest_list);
|
|
if (R == NULL) return NULL;
|
|
inbuild_copy *C = R->copy;
|
|
return KitManager::from_copy(C);
|
|
}
|
|
|
|
@ The ITTT process for a project calls this to see if the ITTT rules for
|
|
a |K| require further kit dependencies to be added to the project: if they
|
|
do, then the dependencies are added and we return |TRUE|. If there was
|
|
nothing to do, we return |FALSE|.
|
|
|
|
=
|
|
int Kits::perform_ittt(inform_kit *K, inform_project *project, int parity) {
|
|
int changes_made = FALSE;
|
|
inform_kit_ittt *ITTT;
|
|
LOOP_OVER_LINKED_LIST(ITTT, inform_kit_ittt, K->ittt)
|
|
if ((ITTT->if_included == parity) &&
|
|
(Projects::uses_kit(project, ITTT->then_name) == FALSE) &&
|
|
(Projects::uses_kit(project, ITTT->if_name) == ITTT->if_included)) {
|
|
Projects::add_kit_dependency(project, ITTT->then_name, NULL, K, NULL, NULL);
|
|
changes_made = TRUE;
|
|
}
|
|
return changes_made;
|
|
}
|
|
|
|
@h Kind definitions.
|
|
The base kinds for the Inform language, such as "real number" or "text", are
|
|
not defined in high-level source text, nor by Inter, but by special configuration
|
|
files held in the |kinds| subdirectory of the kits used. The following function
|
|
loads the base kinds in a kit |K|:
|
|
|
|
=
|
|
#ifdef CORE_MODULE
|
|
void Kits::load_built_in_kind_constructors(inform_kit *K) {
|
|
text_stream *segment;
|
|
LOOP_OVER_LINKED_LIST(segment, text_stream, K->kind_definitions) {
|
|
pathname *P = Pathnames::down(K->as_copy->location_if_path, I"kinds");
|
|
filename *F = Filenames::in(P, segment);
|
|
LOG("Loading kinds definitions from %f\n", F);
|
|
Kits::interpret_neptune(F);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
@ Using this rudimentary interpreter:
|
|
|
|
=
|
|
#ifdef CORE_MODULE
|
|
void Kits::interpret_neptune(filename *neptune_file) {
|
|
FILE *Input_File = NULL;
|
|
int col = 1, cr, lc = 0;
|
|
TEMPORARY_TEXT(heading_name)
|
|
@<Open a file for input, if necessary@>;
|
|
TEMPORARY_TEXT(command)
|
|
TEMPORARY_TEXT(argument)
|
|
do {
|
|
Str::clear(command);
|
|
Str::clear(argument);
|
|
@<Read next character from I6T stream@>;
|
|
if (cr == EOF) break;
|
|
lc++;
|
|
if ((cr == 10) || (cr == 13)) continue; /* skip blank lines here */
|
|
@<Read rest of line as argument@>;
|
|
if ((Str::get_first_char(argument) == '!') ||
|
|
(Str::get_first_char(argument) == 0)) continue; /* skip blanks and comments */
|
|
text_file_position tfp = TextFiles::at(neptune_file, lc);
|
|
parse_node *cs = current_sentence;
|
|
current_sentence = NULL;
|
|
NeptuneFiles::read_command(argument, &tfp);
|
|
current_sentence = cs;
|
|
} while (cr != EOF);
|
|
DISCARD_TEXT(command)
|
|
DISCARD_TEXT(argument)
|
|
if (Input_File) { if (DL) STREAM_FLUSH(DL); fclose(Input_File); }
|
|
DISCARD_TEXT(heading_name)
|
|
}
|
|
#endif
|
|
|
|
@<Open a file for input, if necessary@> =
|
|
if (neptune_file) {
|
|
Input_File = Filenames::fopen(neptune_file, "r");
|
|
if (Input_File == NULL) {
|
|
LOG("Filename was %f\n", neptune_file);
|
|
StandardProblems::unlocated_problem(Task::syntax_tree(),
|
|
_p_(BelievedImpossible), /* or anyway not usefully testable */
|
|
"I couldn't open a Neptune file for defining built-in kinds.");
|
|
}
|
|
}
|
|
|
|
@ I6 template files are encoded as ISO Latin-1, not as Unicode UTF-8, so
|
|
ordinary |fgetc| is used, and no BOM marker is parsed. Lines are assumed
|
|
to be terminated with either |0x0a| or |0x0d|. (Since blank lines are
|
|
harmless, we take no trouble over |0a0d| or |0d0a| combinations.) The
|
|
built-in template files, almost always the only ones used, are line
|
|
terminated |0x0a| in Unix fashion.
|
|
|
|
@<Read next character from I6T stream@> =
|
|
if (Input_File) cr = fgetc(Input_File);
|
|
else cr = EOF;
|
|
col++; if ((cr == 10) || (cr == 13)) col = 0;
|
|
|
|
@ We get here when reading a kinds template file. Note that initial and
|
|
trailing white space on the line is deleted: this makes it easier to lay
|
|
out I6T template files tidily.
|
|
|
|
@<Read rest of line as argument@> =
|
|
Str::clear(argument);
|
|
if (Characters::is_space_or_tab(cr) == FALSE) PUT_TO(argument, cr);
|
|
int at_start = TRUE;
|
|
while (TRUE) {
|
|
@<Read next character from I6T stream@>;
|
|
if ((cr == 10) || (cr == 13) || (cr == EOF)) break;
|
|
if ((at_start) && (Characters::is_space_or_tab(cr))) continue;
|
|
PUT_TO(argument, cr); at_start = FALSE;
|
|
}
|
|
while (Characters::is_space_or_tab(Str::get_last_char(argument)))
|
|
Str::delete_last_character(argument);
|
|
|
|
@h Language element activation.
|
|
Note that this function is meaningful only when this module is part of the
|
|
|inform7| executable, and it invites us to activate or deactivate language
|
|
features as |K| would like.
|
|
|
|
=
|
|
void Kits::activate_elements(inform_kit *K) {
|
|
element_activation *EA;
|
|
LOOP_OVER_LINKED_LIST(EA, element_activation, K->activations) {
|
|
compiler_feature *P = Features::from_name(EA->element_name);
|
|
if (P == NULL) {
|
|
TEMPORARY_TEXT(err)
|
|
WRITE_TO(err, "kit metadata refers to unknown compiler feature '%S'", EA->element_name);
|
|
Copies::attach_error(K->as_copy, CopyErrors::new_T(METADATA_MALFORMED_CE, -1, err));
|
|
DISCARD_TEXT(err)
|
|
} else {
|
|
if (EA->activate) Features::activate(P);
|
|
else if (Features::deactivate(P) == FALSE) {
|
|
TEMPORARY_TEXT(err)
|
|
WRITE_TO(err, "kit metadata asks to deactivate mandatory compiler feature '%S'",
|
|
EA->element_name);
|
|
Copies::attach_error(K->as_copy, CopyErrors::new_T(METADATA_MALFORMED_CE, -1, err));
|
|
DISCARD_TEXT(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@h Early source.
|
|
As we have seen, kits can ask for extensions to be included.
|
|
|
|
As a last resort, a kit can also ask for a sentence or two to be mandatorily
|
|
included in the source text for any project using it. This text appears very
|
|
early on, and can't do much, but could for example set use options.
|
|
|
|
This function simply writes out such sentences, so that they can be fed
|
|
into the lexer by our caller.
|
|
|
|
=
|
|
void Kits::early_source_text(OUTPUT_STREAM, inform_kit *K) {
|
|
inbuild_requirement *req;
|
|
LOOP_OVER_LINKED_LIST(req, inbuild_requirement, K->extensions) {
|
|
WRITE("Include ");
|
|
if (VersionNumberRanges::is_any_range(req->version_range) == FALSE) {
|
|
semantic_version_number V = req->version_range->lower.end_value;
|
|
WRITE("version %v of ", &V);
|
|
}
|
|
WRITE("%S by %S.\n\n", req->work->title, req->work->author_name);
|
|
}
|
|
if (K->early_source) WRITE("%S\n\n", K->early_source);
|
|
}
|
|
|
|
linked_list *Kits::inter_paths(linked_list *L) {
|
|
linked_list *inter_paths = NEW_LINKED_LIST(pathname);
|
|
inbuild_nest *N;
|
|
LOOP_OVER_LINKED_LIST(N, inbuild_nest, L)
|
|
ADD_TO_LINKED_LIST(KitManager::path_within_nest(N), pathname, inter_paths);
|
|
return inter_paths;
|
|
}
|
|
|
|
@h Build graph.
|
|
The build graph for a kit is quite extensive, since a kit contains Inter
|
|
binaries for four different architectures; and each of those has a
|
|
dependency on every section file of the web of Inform 6 source for the kit.
|
|
If there are $S$ sections then the graph has $S+5$ vertices and $4(S+1)$ edges.
|
|
|
|
Note that ITTT rules do not affect the build graph; they affect only how a
|
|
project uses the kit, and therefore they affect the project's build graph but
|
|
not ours.
|
|
|
|
=
|
|
void Kits::construct_graph(inform_kit *K) {
|
|
RUN_ONLY_FROM_PHASE(GRAPH_CONSTRUCTION_INBUILD_PHASE)
|
|
if (K == NULL) return;
|
|
inbuild_copy *C = K->as_copy;
|
|
pathname *P = C->location_if_path;
|
|
build_vertex *KV = C->vertex; /* the kit vertex */
|
|
linked_list *BVL = NEW_LINKED_LIST(build_vertex); /* list of vertices for the binaries */
|
|
@<Add build edges to the binaries for each architecture@>;
|
|
|
|
web_md *Wm = WebMetadata::get_without_modules(C->location_if_path, NULL);
|
|
|
|
build_vertex *CV = Graphs::file_vertex(Wm->contents_filename); /* the contents page vertex */
|
|
@<Add build edges from the binary vertices to the contents vertex@>;
|
|
|
|
@<Add build edges from the binary vertices to each section vertex@>;
|
|
|
|
inbuild_requirement *req;
|
|
LOOP_OVER_LINKED_LIST(req, inbuild_requirement, K->extensions)
|
|
Kits::add_extension_dependency(KV, req);
|
|
}
|
|
|
|
@ The test for nest protection here ensures that a kit in an internal nest
|
|
will never be incrementally rebuilt in a normal Inform build process, even if
|
|
the timestamps on the files look as if it should be. This is important because
|
|
of the way Linux apps are sandboxed, because a security feature on Linux means
|
|
that timestamps are deliberately reported incorrectly. In any case, the
|
|
internal nest shouldn't be written to even on other platforms.
|
|
|
|
@<Add build edges to the binaries for each architecture@> =
|
|
inter_architecture *A;
|
|
LOOP_OVER(A, inter_architecture)
|
|
if (Compatibility::test_architecture(K->as_copy->edition->compatibility, A)) {
|
|
filename *F = Architectures::canonical_binary(P, A);
|
|
build_vertex *BV = Graphs::file_vertex(F);
|
|
if ((C->nest_of_origin) && (Nests::is_protected(C->nest_of_origin)))
|
|
BV->never_build_this = TRUE;
|
|
else @<Check the Inter version used any already-existing binary file@>;
|
|
Graphs::need_this_to_build(KV, BV);
|
|
BuildSteps::attach(BV, build_kit_using_inter_skill, FALSE, NULL, A, K->as_copy);
|
|
ADD_TO_LINKED_LIST(BV, build_vertex, BVL);
|
|
}
|
|
|
|
@<Add build edges from the binary vertices to the contents vertex@> =
|
|
build_vertex *BV;
|
|
LOOP_OVER_LINKED_LIST(BV, build_vertex, BVL)
|
|
Graphs::need_this_to_build(BV, CV);
|
|
|
|
@<Add build edges from the binary vertices to each section vertex@> =
|
|
chapter_md *Cm;
|
|
LOOP_OVER_LINKED_LIST(Cm, chapter_md, Wm->chapters_md) {
|
|
section_md *Sm;
|
|
LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md) {
|
|
filename *SF = Sm->source_file_for_section;
|
|
build_vertex *SV = Graphs::file_vertex(SF);
|
|
build_vertex *BV;
|
|
LOOP_OVER_LINKED_LIST(BV, build_vertex, BVL)
|
|
Graphs::need_this_to_build(BV, SV);
|
|
}
|
|
}
|
|
|
|
@ Suppose the user has an old kit lying around, and it has been pre-built but
|
|
back in the days of Inter version 17.1 when we're now in the brave new world of
|
|
Inter 19.3. Because the source files for the kit are even older than its
|
|
old binary form, the datestamps will not cause the kit to be rebuilt. Inform
|
|
will then try to load the binary, and fail because its version is out of date.
|
|
We want to avoid all that by forcing a rebuild, regardless of the source
|
|
datestamps, if (a) the binary exists but (b) it uses an obsolete Inter version.
|
|
|
|
@<Check the Inter version used any already-existing binary file@> =
|
|
#ifdef BYTECODE_MODULE
|
|
semantic_version_number V = BinaryInter::test_file_version(F);
|
|
if ((VersionNumbers::is_null(V) == FALSE) &&
|
|
(InterVersion::check_readable(V) == FALSE)) {
|
|
semantic_version_number current_version = InterVersion::current();
|
|
LOG("Forcing rebuild of '%f' because it uses Inter v%v, and we want %v\n",
|
|
F, &V, ¤t_version);
|
|
BV->always_build_this = TRUE;
|
|
}
|
|
#endif
|
|
|
|
@ Suppose our kit wants to include Locksmith by Emily Short. If that's an
|
|
extension we have already read in, we can place a use edge to its existing
|
|
build vertex. If not, the best we can do is a use edge to a requirement
|
|
vertex, i.e., to a vertex meaning "we would like Locksmith but can't find it".
|
|
|
|
=
|
|
void Kits::add_extension_dependency(build_vertex *KV, inbuild_requirement *req) {
|
|
build_vertex *RV = NULL;
|
|
inform_extension *E;
|
|
LOOP_OVER(E, inform_extension)
|
|
if (Requirements::meets(E->as_copy->edition, req)) {
|
|
RV = E->as_copy->vertex;
|
|
build_vertex *V;
|
|
int N = 0;
|
|
LOOP_OVER_LINKED_LIST(V, build_vertex, KV->use_edges) {
|
|
if ((V->type == REQUIREMENT_VERTEX) &&
|
|
(Requirements::meets(E->as_copy->edition, V->as_requirement))) {
|
|
LinkedLists::delete(N, KV->use_edges);
|
|
break;
|
|
}
|
|
N++;
|
|
}
|
|
break;
|
|
}
|
|
if (RV == NULL) {
|
|
build_vertex *V;
|
|
int N = 0;
|
|
LOOP_OVER_LINKED_LIST(V, build_vertex, KV->use_edges) {
|
|
if ((V->type == REQUIREMENT_VERTEX) &&
|
|
(Requirements::trumps(req, V->as_requirement))) {
|
|
LinkedLists::delete(N, KV->use_edges);
|
|
break;
|
|
}
|
|
N++;
|
|
}
|
|
RV = Graphs::req_vertex(req);
|
|
}
|
|
Graphs::need_this_to_use(KV, RV);
|
|
}
|
|
|
|
@ We can find a configuration symbol as used by a kit, returning |TRUE| if
|
|
it is a flag, |FALSE| if a value, and |NOT_APPLICABLE| if it doesn't exist
|
|
for the kit:
|
|
|
|
=
|
|
int Kits::configuration_is_a_flag(inform_kit *kit, text_stream *name) {
|
|
kit_configuration *kc;
|
|
LOOP_OVER_LINKED_LIST(kc, kit_configuration, kit->configurations)
|
|
if (Str::eq_insensitive(kc->symbol_name, name))
|
|
return kc->is_flag;
|
|
return NOT_APPLICABLE;
|
|
}
|