1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-06-26 04:00:43 +03:00

Tidied up further

This commit is contained in:
Graham Nelson 2020-02-26 19:58:32 +00:00
parent edc97dcf9c
commit dfc716b4f6
6 changed files with 188 additions and 192 deletions

View file

@ -50,6 +50,8 @@ to add and process command line switches handled by inbuild:
@e PIPELINE_CLSW
@e PIPELINE_FILE_CLSW
@e PIPELINE_VARIABLE_CLSW
@e RNG_CLSW
@e CASE_CLSW
=
void Inbuild::declare_options(void) {
@ -83,6 +85,10 @@ void Inbuild::declare_options(void) {
L"specify code-generation pipeline from file X");
CommandLine::declare_switch(PIPELINE_VARIABLE_CLSW, L"variable", 2,
L"set pipeline variable X (in form name=value)");
CommandLine::declare_boolean_switch(RNG_CLSW, L"rng", 1,
L"fix the random number generator of the story file (for testing)");
CommandLine::declare_switch(CASE_CLSW, L"case", 2,
L"make any source links refer to the source in extension example X");
CommandLine::end_group();
Inbuild::set_defaults();
@ -96,6 +102,7 @@ int this_is_a_debug_compile = FALSE; /* Destined to be compiled with debug featu
int this_is_a_release_compile = FALSE; /* Omit sections of source text marked not for release */
text_stream *story_filename_extension = NULL; /* What story file we will eventually have */
int census_mode = FALSE; /* Running only to update extension documentation */
int rng_seed_at_start_of_play = 0; /* The seed value, or 0 if not seeded */
void Inbuild::set_defaults(void) {
inter_processing_pipeline = Str::new();
@ -142,6 +149,11 @@ void Inbuild::option(int id, int val, text_stream *arg, void *state) {
Regexp::dispose_of(&mr);
break;
}
case RNG_CLSW:
if (val) rng_seed_at_start_of_play = -16339;
else rng_seed_at_start_of_play = 0;
break;
case CASE_CLSW: HTMLFiles::set_source_link_case(arg); break;
}
}
@ -220,6 +232,27 @@ inform_project *Inbuild::go_operational(void) {
}
@h The nest list.
Nests are directories which hold resources to be used by the Intools, and
one of Inbuild's main roles is to search and manage nests. All nests can
hold extensions, kits, language definitions, and so on.
But among nests three are special, and can hold other things as well.
(a) The "internal" nest is part of the installation of Inform as software.
It contains, for example, the build-in extensions. But it also contains
miscellaneous other files needed by Infomr (see below).
(b) The "external" nest is the one to which the user installs her own
selection of extensions, and so on. On most platforms, the external nest
is also the default home of "transient" storage, for more ephemeral content,
such as the mechanically generated extension documentation. Some mobile
operating systems are aggressive about wanting to delete ephemeral files
used by applications, so |-transient| can be used to divert these.
(c) Every project has its own private nest, in the form of its associated
Materials folder. For example, in |Jane Eyre.inform| is a project, then
alongside it is |Jane Eyre.materials| and this is a nest.
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
@ -308,8 +341,8 @@ pathname *Inbuild::materials(void) {
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
@ As noted above, 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.
=
@ -399,6 +432,8 @@ inform_project *Inbuild::create_shared_project(inbuild_copy *C) {
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);
if (rng_seed_at_start_of_play != 0)
Projects::fix_rng(shared_project, rng_seed_at_start_of_play);
}
return shared_project;
}
@ -486,9 +521,30 @@ void Inbuild::pass_kit_requests(void) {
}
@h Installation.
Inform and its associated tools need to be able to see certain files stored
in the internal nest: if they aren't there, then Inform is not properly
installed on disc.
Inform needs a whole pile of files to have been installed on the host computer
before it can run: everything from the Standard Rules to a PDF file explaining
what interactive fiction is. They're never written to, only read. They are
referred to as "internal" or "built-in", and they occupy a folder called the
"internal resources" folder.
Unfortunately we don't know where it is. Typically this compiler will be an
executable sitting somewhere inside a user interface application, and the
internal resources folder will be somewhere else inside it. But we don't
know how to find that folder, and we don't want to make any assumptions.
This is the purpose of the internal nest, and this is why inform7 can only
be run if an |-internal| switch has been used specifying where it is.
The internal nest has two additional subfolders (additional in that they
don't hold copies in the Inbuild sense, just a bunch of loose odds and ends):
|Miscellany| and |HTML|. Many of these files are to help Inblorb to perform
a release.
The documentation snippets file is generated by |indoc| and contains
brief specifications of phrases, extracted from the manual "Writing with
Inform". This is used to generate the Phrasebook index.
Anyway, Inform and its associated tools can then access these files using the
following routine:
@e CBLORB_REPORT_MODEL_IRES from 1
@e DOCUMENTATION_SNIPPETS_IRES
@ -510,19 +566,20 @@ filename *Inbuild::file_from_installation(int ires) {
pathname *misc = Pathnames::subfolder(I->location, I"Miscellany");
pathname *models = Pathnames::subfolder(I->location, I"HTML");
switch (ires) {
case CBLORB_REPORT_MODEL_IRES: return Filenames::in_folder(models, I"CblorbModel.html");
case DOCUMENTATION_SNIPPETS_IRES: return Filenames::in_folder(misc, I"definitions.html");
case INTRO_BOOKLET_IRES: return Filenames::in_folder(misc, I"IntroductionToIF.pdf");
case INTRO_POSTCARD_IRES: return Filenames::in_folder(misc, I"Postcard.pdf");
case LARGE_DEFAULT_COVER_ART_IRES: return Filenames::in_folder(misc, I"Cover.jpg");
case SMALL_DEFAULT_COVER_ART_IRES: return Filenames::in_folder(misc, I"Small Cover.jpg");
case CBLORB_REPORT_MODEL_IRES: return Filenames::in_folder(models, I"CblorbModel.html");
case DOCUMENTATION_XREFS_IRES: return Filenames::in_folder(models, I"xrefs.txt");
case JAVASCRIPT_FOR_STANDARD_PAGES_IRES: return Filenames::in_folder(models, I"main.js");
case JAVASCRIPT_FOR_EXTENSIONS_IRES: return Filenames::in_folder(models, I"extensions.js");
case JAVASCRIPT_FOR_ONE_EXTENSION_IRES: return Filenames::in_folder(models, I"extensionfile.js");
case CSS_FOR_STANDARD_PAGES_IRES: return Filenames::in_folder(models, I"main.css");
case EXTENSION_DOCUMENTATION_MODEL_IRES: return Filenames::in_folder(models, I"extensionfile.html");
}
}
internal_error("unknown installation resource file");
return NULL;
}

View file

@ -15,6 +15,7 @@ typedef struct inform_project {
struct build_vertex *unblorbed_vertex;
struct build_vertex *blorbed_vertex;
struct build_vertex *chosen_build_target;
int fix_rng;
MEMORY_MANAGEMENT
} inform_project;
@ -31,6 +32,7 @@ inform_project *Projects::new_ip(text_stream *name, filename *F, pathname *P) {
project->chosen_build_target = NULL;
project->unblorbed_vertex = NULL;
project->blorbed_vertex = NULL;
project->fix_rng = 0;
return project;
}
@ -79,6 +81,10 @@ inform_language *Projects::get_language_of_syntax(inform_project *proj) {
return proj->language_of_syntax;
}
void Projects::fix_rng(inform_project *project, int seed) {
project->fix_rng = seed;
}
void Projects::not_necessarily_parser_IF(inform_project *project) {
project->assumed_to_be_parser_IF = FALSE;
}

View file

@ -9,7 +9,6 @@ options with inbuild: see that module for more.
=
int existing_story_file = FALSE; /* Ignore source text to blorb existing story file? */
int rng_seed_at_start_of_play = 0; /* The seed value, or 0 if not seeded */
int show_progress_indicator = TRUE; /* Produce percentage of progress messages */
int scoring_option_set = NOT_APPLICABLE; /* Whether in this case a score is kept at run time */
int do_not_generate_index = FALSE; /* Set by the |-noindex| command line option */
@ -84,6 +83,11 @@ int CoreMain::main(int argc, char *argv[]) {
Problems::Issue::start_problems_report(PF);
@ Telemetry is not as sinister as it sounds: the app isn't sending data out
on the Internet, only (if requested) logging what it's doing to a local file.
This was provided for classroom use, so that teachers can see what their
students have been getting stuck on.
@<Open the telemetry@> =
pathname *P = Pathnames::subfolder(Inbuild::transient(), I"Telemetry");
if (Pathnames::create_in_file_system(P)) {
@ -131,12 +135,10 @@ int CoreMain::read_command_line(int argc, char *argv[]) {
arguments in order to set certain pathnames or filenames, so the following
list is not exhaustive.
@e CASE_CLSW
@e CRASHALL_CLSW
@e NOINDEX_CLSW
@e NOPROGRESS_CLSW
@e REQUIRE_PROBLEM_CLSW
@e RNG_CLSW
@e SIGILS_CLSW
@<Register command-line arguments@> =
@ -150,12 +152,8 @@ list is not exhaustive.
L"don't produce an Index");
CommandLine::declare_boolean_switch(NOPROGRESS_CLSW, L"noprogress", 1,
L"don't display progress percentages");
CommandLine::declare_boolean_switch(RNG_CLSW, L"rng", 1,
L"fix the random number generator of the story file (for testing)");
CommandLine::declare_boolean_switch(SIGILS_CLSW, L"sigils", 1,
L"print Problem message sigils (for testing)");
CommandLine::declare_switch(CASE_CLSW, L"case", 2,
L"make any source links refer to the source in extension example X");
CommandLine::declare_switch(REQUIRE_PROBLEM_CLSW, L"require-problem", 2,
L"return 0 unless exactly this Problem message is generated (for testing)");
Inbuild::declare_options();
@ -163,18 +161,10 @@ list is not exhaustive.
@=
void CoreMain::switch(int id, int val, text_stream *arg, void *state) {
switch (id) {
/* Miscellaneous boolean settings */
case CRASHALL_CLSW: debugger_mode = val; crash_on_all_errors = val; break;
case NOINDEX_CLSW: do_not_generate_index = val; break;
case NOPROGRESS_CLSW: show_progress_indicator = val?FALSE:TRUE; break;
case RNG_CLSW:
if (val) rng_seed_at_start_of_play = -16339;
else rng_seed_at_start_of_play = 0;
break;
case SIGILS_CLSW: echo_problem_message_sigils = val; break;
/* Other settings */
case CASE_CLSW: HTMLFiles::set_source_link_case(arg); break;
case REQUIRE_PROBLEM_CLSW: Problems::Fatal::require(arg); break;
}
Inbuild::option(id, val, arg, state);

View file

@ -97,7 +97,47 @@ int Task::get_next_free_blorb_resource_ID(void) {
return inform7_task->next_resource_number++;
}
@ This seed is ordinarily 0, causing no fix to occur, but can be set to
a non-zero value to make testing Inform easier.
=
int Task::rng_seed(void) {
if (inform7_task == NULL) internal_error("there is no current task");
return inform7_task->project->fix_rng;
}
@h Project-related files and file paths.
An Inform compilation can touch dozens of different files, and the rest
of this section is a tour through the ones which are associated with the
project itself. (Common resources, used for all compilations, or optional
add-ins such as extensions are the business of Inbuild.)
If a project is called, say, Wuthering Heights, and is a "bundle" as created
and compiled by the Inform app, then:
(a) The project path will be |Wuthering Heights.inform|. This looks opaque
on MacOS, as if a file, but on all platforms it is in fact a directory.
(b) Within it is |Wuthering Heights.inform/Build|, the "build folder".
(c) Alongside it is |Wuthering Heights.materials|. This is also a directory,
but is openly accessible even on MacOS.
If Inform is working on a single source text file, not a bundle, then the
project will be the current working directory, but now the build folder will
be the Inbuild transient area, and materials (if present) will again be
alongside.
To begin: what's in the project area? |story.ni| and |auto.inf|, neither
one very helpfully named, are defined in Inbuild rather than here: these
are the I7 source text and its compilation down to I6, respectively.
In addition we have:
The UUID file records an ISBN-like identifying number for the project. This
is read-only for us.
The iFiction record, manifest and blurb file are all files that we generate
to give instructions to the releasing agent Inblorb. This means that they
have no purpose unless Inform is in a release run (with |-release| set on
the command line), but they take no time to generate so we make them anyway.
=
filename *Task::uuid_file(void) {
@ -117,6 +157,12 @@ filename *Task::blurb_file(void) {
return Filenames::in_folder(inform7_task->path, I"Release.blurb");
}
@ The build folder for a project contains all of the working files created
during the compilation process. The debugging log and Inform problems report
(its HTML file of error messages) are both written there: see the Main Routine
section for details. In addition we have:
=
filename *Task::cblorb_report_file(void) {
if (inform7_task == NULL) internal_error("there is no current task");
return Filenames::in_folder(inform7_task->build, I"StatusCblorb.html");
@ -125,6 +171,11 @@ filename *Task::parse_tree_file(void) {
if (inform7_task == NULL) internal_error("there is no current task");
return Filenames::in_folder(inform7_task->build, I"Parse tree.txt");
}
@ The name of the unblorbed story file is chosen for us by Inbuild, so
we have to extract it from the build graph:
=
filename *Task::storyfile_file(void) {
if (inform7_task == NULL) internal_error("there is no current task");
build_vertex *V = inform7_task->project->unblorbed_vertex;
@ -132,11 +183,11 @@ filename *Task::storyfile_file(void) {
return V->buildable_if_internal_file;
}
@ Location of index files.
Filenames within the |Index| subfolder. Filenames in |Details| have the form
|N_S| where |N| is the integer supplied and |S| the leafname; for instance,
|21_A.html| provides details page number 21 about actions, derived from the
leafname |A.html|.
@ Deeper inside the|Build| subfolder is an (also ephemeral) |Index| subfolder,
which holds the mini-website of the Index for a project.
The main index files (|Phrasebook.html| and so on) live at the top level,
details on actions live in the subfolder |Details|: see below.
=
pathname *Task::index_path(void) {
@ -145,14 +196,34 @@ pathname *Task::index_path(void) {
if (Pathnames::create_in_file_system(P)) return P;
return NULL;
}
@ An oddity in the Index folder is an XML file recording where the headings
are in the source text: this is for the benefit of the user interface
application, if it wants it, but is not linked to or used by the HTML of
the index as seen by the user.
=
filename *Task::xml_headings_file(void) {
return Filenames::in_folder(Task::index_path(), I"Headings.xml");
}
@ Within the Index is a deeper level, into the weeds as it were, called
|Details|.
=
pathname *Task::index_details_path(void) {
pathname *P = Pathnames::subfolder(Task::index_path(), I"Details");
if (Pathnames::create_in_file_system(P)) return P;
return NULL;
}
filename *Task::xml_headings_file(void) {
return Filenames::in_folder(Task::index_path(), I"Headings.xml");
}
@ And the following routine determines the filename for a page in this
mini-website. Filenames down in the |Details| area have the form
|N_S| where |N| is an integer supplied and |S| the leafname; for instance,
|21_A.html| provides details page number 21 about actions, derived from the
leafname |A.html|.
=
filename *Task::index_file(text_stream *leafname, int sub) {
if (sub >= 0) {
TEMPORARY_TEXT(full_leafname);
@ -165,6 +236,21 @@ filename *Task::index_file(text_stream *leafname, int sub) {
}
}
@ That's it for the project folder, but other project-related stuff is in
the materials folder, which we turn to next.
Inform is occasionally run in a mode where it performs a release on an
existing story file (for example a 1980s Infocom one) rather than on one
that it has newly generated. This is the filename such a story file would
have by default, if so.
By default the story file will be called something like |story.z8|, but
its leafname is actually declared from the source text of the Inform
project created to do this wrapping-up. So we need a way to set as well
as read this filename. Whatever the leafname, though, it lives in the top
level of materuals.
=
void Task::set_existing_storyfile(text_stream *name) {
if (inform7_task == NULL) internal_error("there is no current task");
if (name == NULL) {
@ -180,6 +266,17 @@ filename *Task::existing_storyfile_file(void) {
if (inform7_task == NULL) internal_error("there is no current task");
return inform7_task->existing_storyfile;
}
@ Materials is also where cover art lives: it could have either the file
extension |.jpg| or |.png|, and we generate both sets of filenames, even
though at most one will actually work. This is also where we generate the EPS
file of the map, if so requested; a bit anomalously, it's the only file in
Materials but outside Release which we write to.
This is also where the originals (not the released copies) of the Figures
and Sounds, if any, live: in their own subfolders.
=
filename *Task::large_cover_art_file(int JPEG) {
if (inform7_task == NULL) internal_error("there is no current task");
if (JPEG) return Filenames::in_folder(inform7_task->materials, I"Cover.jpg");
@ -199,6 +296,12 @@ pathname *Task::sounds_path(void) {
return Pathnames::subfolder(inform7_task->materials, I"Sounds");
}
@ On a release run, Inblorb will populate the Release subfolder of Materials;
figures and sounds will be copied into the relevant subfolders. The principle
is that everything in Release can always be thrown away without loss, because
it can all be generated again.
=
pathname *Task::release_path(void) {
if (inform7_task == NULL) internal_error("there is no current task");
return Pathnames::subfolder(inform7_task->materials, I"Release");
@ -212,163 +315,3 @@ pathname *Task::released_sounds_path(void) {
pathname *Task::released_interpreter_path(void) {
return Pathnames::subfolder(Task::release_path(), I"interpreter");
}
@h Establishing the defaults.
Inform's file access happens inside four different areas: the internal
resources area, usually inside the Inform application; the external resources
area, which is where the user (or the application acting on the user's behalf)
installs extensions; the project bundle, say |Example.inform|; and, alongside
that, the materials folder, |Example.materials|.
=
int Task::set_more_defaults(void) {
@<Internal resources@>;
@<External resources@>;
@<Project resources@>;
@<Materials resources@>;
return TRUE;
}
@h Internal resources.
Inform needs a whole pile of files to have been installed on the host computer
before it can run: everything from the Standard Rules to a PDF file explaining
what interactive fiction is. They're never written to, only read. They are
referred to as "internal" or "built-in", and they occupy a folder called the
"internal resources" folder.
Unfortunately we don't know where it is. Typically this compiler will be an
executable sitting somewhere inside a user interface application, and the
internal resources folder will be somewhere else inside it. But we don't
know how to find that folder, and we don't want to make any assumptions.
Inform therefore requires on every run that it be told via the |-internal|
switch where the internal resources folder is.
@<Internal resources@> =
@<Miscellaneous other stuff@>;
@ Most of these files are to help Inblorb to perform a release. The
documentation models are used when making extension documentation; the
leafname is platform-dependent so that Windows can use different models
from everybody else.
The documentation snippets file is generated by |indoc| and contains
brief specifications of phrases, extracted from the manual "Writing with
Inform". This is used to generate the Phrasebook index.
@<Miscellaneous other stuff@> =
;
@h External resources.
This is where the user can install downloaded extensions, new interpreters,
website templates and so on; so-called "permanent" external resources, since
the user expects them to stay put once installed. But there is also a
"transient" external resources area, for more ephemeral content, such as
the mechanically generated extension documentation. On most platforms the
permanent and transient external areas will be the same, but some mobile
operating systems are aggressive about wanting to delete ephemeral files
used by applications.
The locations of the permanent and transient external folders can be set
using |-external| and |-transient| respectively. If no |-external| is
specified, the location depends on the platform settings: for example on
Mac OS X it will typically be
|/Library/Users/hclinton/Library/Inform|
If |-transient| is not specified, it's the same folder, i.e., Inform does
not distinguish between permanent and transient external resources.
@<External resources@> =
@<Transient telemetry@>;
@ Telemetry is not as sinister as it sounds: the app isn't sending data out
on the Internet, only (if requested) logging what it's doing to a local file.
This was provided for classroom use, so that teachers can see what their
students have been getting stuck on.
@<Transient telemetry@> =
;
@h Project resources.
Although on some platforms it may look like a single file, an Inform project
is a folder whose name has the dot-extension |.inform|. We'll call this the
"project folder", and it contains a whole bundle of useful files.
The UUID file records an ISBN-like identifying number for the project. This
is read-only for us.
The iFiction record, manifest and blurb file are all files that we generate
to give instructions to the releasing agent Inblorb. This means that they
have no purpose unless Inform is in a release run (with |-release| set on
the command line), but they take no time to generate so we make them anyway.
@<Project resources@> =
@<The Build folder within the project@>;
@<The Index folder within the project@>;
@ The build folder for a project contains all of the working files created
during the compilation process. The opening part here may be a surprise:
In extension census mode, Inform is running not to compile something but to
extract details of all the extensions installed. But it still needs somewhere
to write its temporary and debugging files, and there is no project bundle
to write into. To get round this, we use the transient data area as if it
were indeed a project bundle.
Briefly: we aim to compile the source text to an Inform 6 program; we issue
an HTML report on our success or failure, listing problem messages if they
occurred; we track our progress in the debugging log. We don't produce the
story file ourselves, I6 will do that, but we do need to know what it's
called; and similarly for the report which the releasing tool Inblorb
will produce if this is a Release run.
@<The Build folder within the project@> =
;
@ We're going to write into the Index folder, so we must ensure it exists.
The main index files (|Phrasebook.html| and so on) live at the top level,
details on actions live in the subfolder |Details|: see below.
An oddity in the Index folder is an XML file recording where the headings
are in the source text: this is for the benefit of the user interface
application, if it wants it, but is not linked to or used by the HTML of
the index as seen by the user.
@<The Index folder within the project@> =
;
@h Materials resources.
@<Materials resources@> =
@<Figures and sounds@>;
@<The Release folder@>;
@<Existing story file@>;
@ This is where cover art lives: it could have either the file extension |.jpg|
or |.png|, and we generate both sets of filenames, even though at most one will
actually work. This is also where we generate the EPS file of the map, if
so requested; a bit anomalously, it's the only file in Materials but outside
Release which we write to.
This is also where the originals (not the released copies) of the Figures
and Sounds, if any, live: in their own subfolders.
@<Figures and sounds@> =
;
@ On a release run, Inblorb will populate the Release subfolder of Materials;
figures and sounds will be copied into the relevant subfolders. The principle
is that everything in Release can always be thrown away without loss, because
it can all be generated again.
@<The Release folder@> =
;
@ Inform is occasionally run in a mode where it performs a release on an
existing story file (for example a 1980s Infocom one) rather than on one
that it has newly generated. This is the filename such a story file would
have by default, if so.
@<Existing story file@> =
;

View file

@ -1089,7 +1089,7 @@ void Kinds::RunTime::compile_instance_counts(void) {
#endif
Kinds::RunTime::compile_nnci(Hierarchy::find(CCOUNT_QUOTATIONS_HL), Strings::TextLiterals::CCOUNT_QUOTATIONS());
Kinds::RunTime::compile_nnci(Hierarchy::find(MAX_FRAME_SIZE_NEEDED_HL), max_frame_size_needed);
Kinds::RunTime::compile_nnci(Hierarchy::find(RNG_SEED_AT_START_OF_PLAY_HL), rng_seed_at_start_of_play);
Kinds::RunTime::compile_nnci(Hierarchy::find(RNG_SEED_AT_START_OF_PLAY_HL), Task::rng_seed());
}
void Kinds::RunTime::compile_data_type_support_routines(void) {

View file

@ -1523,7 +1523,7 @@ suite: it would be annoying to verify this problem message otherwise.)
@<Choose random antagonists for variety@> =
char *P, *Q, *In;
int variant = 0;
if (rng_seed_at_start_of_play == 0) variant = (time(0))&15;
if (Task::rng_seed() == 0) variant = (time(0))&15;
switch(variant) {
case 1: P = "the chalk"; Q = "the cheese"; In = "Dairy Products School"; break;
case 2: P = "St Peter"; Q = "St Paul"; In = "Pearly Gates"; break;