1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-05 08:34:22 +03:00
inform7/inbuild/inbuild-module/Chapter 1/Shared CLI.w

315 lines
11 KiB
OpenEdge ABL
Raw Normal View History

2020-02-10 12:27:24 +02:00
[SharedCLI::] Shared CLI.
A subset of command-line options shared by the tools which incorporate this
module.
2020-02-13 01:48:37 +02:00
@h Using this API.
The Inbuild module has to be used in three phases, as follows:
@e CONFIGURATION_INBUILD_PHASE from 1
@e TINKERING_INBUILD_PHASE
@e GRAPHING_INBUILD_PHASE
=
int inbuild_phase = CONFIGURATION_INBUILD_PHASE;
@h The nest list.
Nests used by the Inform and Inbuild tools are tagged with the following
comstamts, except that no nest is ever tagged |NOT_A_NEST_TAG|.
(There used to be quite a good joke here, but refactoring of the
code removed its premiss. Literate programming is like that sometimes.)
2020-02-10 12:27:24 +02:00
@e NOT_A_NEST_TAG from 0
@e MATERIALS_NEST_TAG
@e EXTERNAL_NEST_TAG
@e GENERIC_NEST_TAG
@e INTERNAL_NEST_TAG
2020-02-10 12:27:24 +02:00
@ Inform customarily has exactly one |-internal| and one |-external| nest,
but in fact any number of each are allowed, including none. However, the
first to be declared are used by the compiler as "the" internal and external
nests, respectively.
2020-02-10 12:27:24 +02:00
The following hold the nests in declaration order.
2020-02-10 12:27:24 +02:00
=
2020-02-10 12:27:24 +02:00
linked_list *unsorted_nest_list = NULL;
inbuild_nest *shared_internal_nest = NULL;
inbuild_nest *shared_external_nest = NULL;
inbuild_nest *shared_materials_nest = NULL;
2020-02-10 12:27:24 +02:00
inbuild_nest *SharedCLI::add_nest(pathname *P, int tag) {
2020-02-13 01:48:37 +02:00
if (inbuild_phase > CONFIGURATION_INBUILD_PHASE) internal_error("too late");
2020-02-10 12:27:24 +02:00
if (unsorted_nest_list == NULL)
unsorted_nest_list = NEW_LINKED_LIST(inbuild_nest);
inbuild_nest *N = Nests::new(P);
Nests::set_tag(N, tag);
ADD_TO_LINKED_LIST(N, inbuild_nest, unsorted_nest_list);
if ((tag == EXTERNAL_NEST_TAG) && (shared_external_nest == NULL))
shared_external_nest = N;
if ((tag == INTERNAL_NEST_TAG) && (shared_internal_nest == NULL))
shared_internal_nest = N;
if (tag == INTERNAL_NEST_TAG) Nests::protect(N);
return N;
}
@ It is then sorted in tag order. This is so that if we look for, say, an
extension with a given name, then results in a project's materials folder
are given precedence over those in the external folder, and so on.
=
linked_list *shared_nest_list = NULL;
void SharedCLI::sort_nest_list(void) {
2020-02-13 01:48:37 +02:00
if (inbuild_phase < TINKERING_INBUILD_PHASE) internal_error("too soon");
if (inbuild_phase > TINKERING_INBUILD_PHASE) internal_error("too late");
shared_nest_list = NEW_LINKED_LIST(inbuild_nest);
inbuild_nest *N;
LOOP_OVER_LINKED_LIST(N, inbuild_nest, unsorted_nest_list)
if (Nests::get_tag(N) == MATERIALS_NEST_TAG)
ADD_TO_LINKED_LIST(N, inbuild_nest, shared_nest_list);
LOOP_OVER_LINKED_LIST(N, inbuild_nest, unsorted_nest_list)
if (Nests::get_tag(N) == EXTERNAL_NEST_TAG)
ADD_TO_LINKED_LIST(N, inbuild_nest, shared_nest_list);
LOOP_OVER_LINKED_LIST(N, inbuild_nest, unsorted_nest_list)
if (Nests::get_tag(N) == GENERIC_NEST_TAG)
ADD_TO_LINKED_LIST(N, inbuild_nest, shared_nest_list);
LOOP_OVER_LINKED_LIST(N, inbuild_nest, unsorted_nest_list)
if (Nests::get_tag(N) == INTERNAL_NEST_TAG)
ADD_TO_LINKED_LIST(N, inbuild_nest, shared_nest_list);
}
@ And the rest of Inform or Inbuild can now use:
=
linked_list *SharedCLI::nest_list(void) {
if (shared_nest_list == NULL) internal_error("nest list never sorted");
return shared_nest_list;
}
2020-02-10 12:27:24 +02:00
inbuild_nest *SharedCLI::internal(void) {
return shared_internal_nest;
}
inbuild_nest *SharedCLI::external(void) {
return shared_external_nest;
}
pathname *SharedCLI::materials(void) {
if (shared_materials_nest == NULL) return NULL;
return shared_materials_nest->location;
}
@ The transient area is used for ephemera such as dynamically written
documentation and telemetry files. |-transient| sets it, but otherwise
the external nest is used.
=
pathname *shared_transient_resources = NULL;
2020-02-10 12:27:24 +02:00
pathname *SharedCLI::transient(void) {
if (shared_transient_resources == NULL)
if (shared_external_nest)
return shared_external_nest->location;
return shared_transient_resources;
}
@h The shared project.
In any single run, each of the Inform tools concerns itself with a single
Inform 7 program. This can be presented to it either in a project bundle
(a directory which contains source, settings, space for an index and for
temporary build files), or as a single file (just a text file containing
source text).
2020-02-10 12:27:24 +02:00
It is also possible o set a folder to be the project bundle, and nevertheless
specify a file somewhere else to be the source text. What you can't do is
specify the bundle twice, or specify the file twice.
2020-02-10 12:27:24 +02:00
=
text_stream *project_bundle_request = NULL;
text_stream *project_file_request = NULL;
int SharedCLI::set_I7_source(text_stream *loc) {
if (Str::len(project_file_request) > 0) return FALSE;
project_file_request = Str::duplicate(loc);
return TRUE;
}
int SharedCLI::set_I7_bundle(text_stream *loc) {
if (Str::len(project_bundle_request) > 0) return FALSE;
project_bundle_request = Str::duplicate(loc);
pathname *pathname_of_bundle = Pathnames::from_text(project_bundle_request);
pathname *materials = SharedCLI::pathname_of_materials(pathname_of_bundle);
TEMPORARY_TEXT(leaf);
WRITE_TO(leaf, "%s-settings.txt", INTOOL_NAME);
filename *expert_settings = Filenames::in_folder(materials, leaf);
LOG("Speculatively %f\n", expert_settings);
if (TextFiles::exists(expert_settings))
CommandLine::also_read_file(expert_settings);
DISCARD_TEXT(leaf);
return TRUE;
}
@ If a bundle is found, then by default the source text within it is called
|story.ni|. The |.ni| is an anachronism now, but at one time stood for
"natural Inform", the working title for Inform 7 in the early 2000s.
=
inform_project *shared_project = NULL;
2020-02-13 01:48:37 +02:00
void SharedCLI::create_shared_project(inbuild_copy *C) {
filename *filename_of_i7_source = NULL;
pathname *pathname_of_bundle = NULL;
if (Str::len(project_bundle_request) > 0) {
pathname_of_bundle = Pathnames::from_text(project_bundle_request);
2020-02-10 12:27:24 +02:00
}
if (Str::len(project_file_request) > 0) {
filename_of_i7_source = Filenames::from_text(project_file_request);
}
2020-02-13 01:48:37 +02:00
if (C) {
pathname_of_bundle = C->location_if_path;
filename_of_i7_source = C->location_if_file;
}
if ((pathname_of_bundle) && (filename_of_i7_source == NULL))
filename_of_i7_source =
Filenames::in_folder(
Pathnames::subfolder(pathname_of_bundle, I"Source"),
I"story.ni");
if (pathname_of_bundle) {
2020-02-13 01:48:37 +02:00
if (C == NULL) C = ProjectBundleManager::claim_folder_as_copy(pathname_of_bundle);
shared_project = ProjectBundleManager::from_copy(C);
} else if (filename_of_i7_source) {
2020-02-13 01:48:37 +02:00
if (C == NULL) C = ProjectFileManager::claim_file_as_copy(filename_of_i7_source);
shared_project = ProjectFileManager::from_copy(C);
}
@<Create the materials nest@>;
if (shared_project) {
pathname *P = (shared_materials_nest)?(shared_materials_nest->location):NULL;
if (P) P = Pathnames::subfolder(P, I"Source");
if (Str::len(project_file_request) > 0) P = NULL;
Projects::set_source_filename(shared_project, P, filename_of_i7_source);
}
}
@<Create the materials nest@> =
pathname *materials = NULL;
if (pathname_of_bundle) {
materials = SharedCLI::pathname_of_materials(pathname_of_bundle);
Pathnames::create_in_file_system(materials);
} else if (filename_of_i7_source) {
materials = Pathnames::from_text(I"inform.materials");
}
if (materials) {
shared_materials_nest = SharedCLI::add_nest(materials, MATERIALS_NEST_TAG);
}
@ And the rest of Inform or Inbuild can now use:
=
inform_project *SharedCLI::project(void) {
return shared_project;
}
@ The materials folder sits alongside the project folder and has the same name,
but ending |.materials| instead of |.inform|.
=
pathname *SharedCLI::pathname_of_materials(pathname *pathname_of_bundle) {
TEMPORARY_TEXT(mf);
WRITE_TO(mf, "%S", Pathnames::directory_name(pathname_of_bundle));
int i = Str::len(mf)-1;
while ((i>0) && (Str::get_at(mf, i) != '.')) i--;
if (i>0) {
Str::truncate(mf, i);
WRITE_TO(mf, ".materials");
}
pathname *materials = Pathnames::subfolder(Pathnames::up(pathname_of_bundle), mf);
DISCARD_TEXT(mf);
return materials;
}
@h Kit requests.
=
linked_list *kits_requested_at_command_line = NULL;
void SharedCLI::request_kit(text_stream *name) {
if (kits_requested_at_command_line == NULL)
kits_requested_at_command_line = NEW_LINKED_LIST(text_stream);
text_stream *kit_name;
LOOP_OVER_LINKED_LIST(kit_name, text_stream, kits_requested_at_command_line)
if (Str::eq(kit_name, name))
return;
ADD_TO_LINKED_LIST(Str::duplicate(name), text_stream, kits_requested_at_command_line);
}
void SharedCLI::pass_kit_requests(void) {
if ((shared_project) && (kits_requested_at_command_line)) {
text_stream *kit_name;
LOOP_OVER_LINKED_LIST(kit_name, text_stream, kits_requested_at_command_line) {
Projects::add_kit_dependency(shared_project, kit_name);
Projects::not_necessarily_parser_IF(shared_project);
}
}
}
@h Command line.
We add the following switches:
@e INBUILD_CLSG
@e NEST_CLSW
@e INTERNAL_CLSW
@e EXTERNAL_CLSW
@e TRANSIENT_CLSW
@e KIT_CLSW
@e PROJECT_CLSW
@e SOURCE_CLSW
=
void SharedCLI::declare_options(void) {
CommandLine::begin_group(INBUILD_CLSG);
CommandLine::declare_switch(NEST_CLSW, L"nest", 2,
L"add the nest at pathname X to the search list");
CommandLine::declare_switch(INTERNAL_CLSW, L"internal", 2,
L"use X as the location of built-in material such as the Standard Rules");
CommandLine::declare_switch(EXTERNAL_CLSW, L"external", 2,
L"use X as the user's home for installed material such as extensions");
CommandLine::declare_switch(TRANSIENT_CLSW, L"transient", 2,
L"use X for transient data such as the extensions census");
CommandLine::declare_switch(KIT_CLSW, L"kit", 2,
L"load the Inform kit called X");
CommandLine::declare_switch(PROJECT_CLSW, L"project", 2,
L"work within the Inform project X");
CommandLine::declare_switch(SOURCE_CLSW, L"source", 2,
L"use file X as the Inform source text");
CommandLine::end_group();
}
void SharedCLI::option(int id, int val, text_stream *arg, void *state) {
switch (id) {
case NEST_CLSW: SharedCLI::add_nest(Pathnames::from_text(arg), GENERIC_NEST_TAG); break;
case INTERNAL_CLSW: SharedCLI::add_nest(Pathnames::from_text(arg), INTERNAL_NEST_TAG); break;
case EXTERNAL_CLSW: SharedCLI::add_nest(Pathnames::from_text(arg), EXTERNAL_NEST_TAG); break;
case TRANSIENT_CLSW: shared_transient_resources = Pathnames::from_text(arg); break;
case KIT_CLSW: SharedCLI::request_kit(arg); break;
case PROJECT_CLSW:
if (SharedCLI::set_I7_bundle(arg) == FALSE)
Errors::fatal_with_text("can't specify the project twice: '%S'", arg);
break;
case SOURCE_CLSW:
if (SharedCLI::set_I7_source(arg) == FALSE)
Errors::fatal_with_text("can't specify the source file twice: '%S'", arg);
break;
}
}
@ The client tool (i.e., Inform or Inbuild) should call this when no further
options remain to be processed.
=
2020-02-13 01:48:37 +02:00
inbuild_copy *SharedCLI::optioneering_complete(inbuild_copy *C) {
SharedCLI::create_shared_project(C);
inbuild_phase = TINKERING_INBUILD_PHASE;
SharedCLI::sort_nest_list();
SharedCLI::pass_kit_requests();
2020-02-13 01:48:37 +02:00
return (shared_project)?(shared_project->as_copy):NULL;
2020-02-10 12:27:24 +02:00
}