inweb-bootstrap/Chapter_6/Makefiles.nw

375 lines
14 KiB
Text
Raw Normal View History

2019-02-04 22:26:45 +00:00
[Makefiles::] Makefiles.
Constructing a suitable makefile for a simple inweb project.
2024-03-09 13:23:53 +00:00
@ \section{Preprocessing.}
2022-04-23 15:13:47 +00:00
We will use //foundation: Preprocessor// with four special macros and one
special loop construct.
2019-02-04 22:26:45 +00:00
2022-04-23 15:13:47 +00:00
For the syntax being worked through, see //Webs, Tangling and Weaving//.
2022-04-21 23:06:45 +00:00
2024-03-09 13:23:53 +00:00
<<*>>=
2022-04-23 23:40:37 +00:00
void Makefiles::write(web *W, filename *prototype, filename *F, module_search *I,
text_stream *platform) {
2022-04-23 15:13:47 +00:00
linked_list *L = NEW_LINKED_LIST(preprocessor_macro);
Preprocessor::new_macro(L,
I"platform-settings", NULL,
Makefiles::platform_settings_expander, NULL);
Preprocessor::new_macro(L,
I"identity-settings", NULL,
Makefiles::identity_settings_expander, NULL);
preprocessor_macro *mf = Preprocessor::new_macro(L,
I"modify-filenames", I"original: ORIGINAL ?suffix: SUFFIX ?prefix: PREFIX",
Makefiles::modify_filenames_expander, NULL);
Preprocessor::do_not_suppress_whitespace(mf);
2022-04-23 15:13:47 +00:00
Preprocessor::new_macro(L,
I"component", I"symbol: SYMBOL webname: WEBNAME path: PATH set: SET type: TYPE",
Makefiles::component_expander, NULL);
Preprocessor::new_macro(L,
I"dependent-files", I"?tool: TOOL ?module: MODULES ?tool-and-modules: BOTH",
Makefiles::dependent_files_expander, NULL);
Preprocessor::new_loop_macro(L,
I"components", I"type: TYPE ?set: SET",
Makefiles::components_expander, NULL);
makefile_specifics *specifics = CREATE(makefile_specifics);
2024-03-09 13:23:53 +00:00
<<Initialise the specific data for makefile-preprocessing>>;
2022-04-23 15:13:47 +00:00
text_stream *header = Str::new();
WRITE_TO(header, "# This makefile was automatically written by inweb -makefile\n");
WRITE_TO(header, "# and is not intended for human editing\n\n");
WRITE_TO(STDOUT, "(Read script from %f)\n", prototype);
2022-04-23 22:41:01 +00:00
Preprocessor::preprocess(prototype, F, header, L,
2022-07-25 22:41:35 +00:00
STORE_POINTER_makefile_specifics(specifics), '#', ISO_ENC);
2022-04-23 15:13:47 +00:00
}
@ We will allow a makescript to declare "components" (webs, really), so we need
a data structure to store those declarations in:
2022-04-21 23:06:45 +00:00
2024-03-09 13:23:53 +00:00
<<*>>=
2022-04-23 13:08:38 +00:00
typedef struct makefile_specifics {
2022-04-23 15:13:47 +00:00
struct web *for_web; /* if one has been set at the command line */
2024-03-09 13:23:53 +00:00
struct dictionary *tools_dictionary; /* components with [[type: tool]] */
struct dictionary *webs_dictionary; /* components with [[type: web]] */
struct dictionary *modules_dictionary; /* components with [[type: module]] */
2020-04-02 12:30:38 +00:00
struct module_search *search_path;
2022-04-23 23:40:37 +00:00
struct text_stream *which_platform;
2022-04-23 13:08:38 +00:00
CLASS_DEFINITION
} makefile_specifics;
2019-02-04 22:26:45 +00:00
2024-03-09 13:23:53 +00:00
<<Initialise the specific data for makefile-preprocessing>>=
2022-04-23 13:08:38 +00:00
specifics->for_web = W;
specifics->tools_dictionary = Dictionaries::new(16, FALSE);
specifics->webs_dictionary = Dictionaries::new(16, FALSE);
specifics->modules_dictionary = Dictionaries::new(16, FALSE);
specifics->search_path = I;
2022-04-23 23:40:37 +00:00
specifics->which_platform = platform;
2019-02-04 22:26:45 +00:00
2024-03-09 13:23:53 +00:00
@ \section{The identity-settings expander.}
2022-04-23 15:13:47 +00:00
2024-03-09 13:23:53 +00:00
<<*>>=
2022-04-23 15:13:47 +00:00
void Makefiles::identity_settings_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
2022-04-23 13:08:38 +00:00
makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics);
text_stream *OUT = PPS->dest;
WRITE("INWEB = "); Makefiles::pathname_slashed(OUT, path_to_inweb); WRITE("/Tangled/inweb\n");
pathname *path_to_intest = Pathnames::down(Pathnames::up(path_to_inweb), I"intest");
WRITE("INTEST = "); Makefiles::pathname_slashed(OUT, path_to_intest); WRITE("/Tangled/intest\n");
if (specifics->for_web) {
WRITE("MYNAME = %S\n", Pathnames::directory_name(specifics->for_web->md->path_to_web));
WRITE("ME = "); Makefiles::pathname_slashed(OUT, specifics->for_web->md->path_to_web);
WRITE("\n");
PPS->last_line_was_blank = FALSE;
2022-04-22 22:54:19 +00:00
}
2019-02-04 22:26:45 +00:00
}
2024-03-09 13:23:53 +00:00
@ \section{The platform-settings expander.}
2022-04-23 15:13:47 +00:00
We first scan Inweb's platform settings file for a definition line in the
shape INWEBPLATFORM = PLATFORM, in order to find out what PLATFORM the make file
will be used on. Then we splice in the appropriate file of standard definitions
for that platform.
2024-03-09 13:23:53 +00:00
<<*>>=
2022-04-23 15:13:47 +00:00
void Makefiles::platform_settings_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
2022-04-23 23:40:37 +00:00
makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics);
text_stream *INWEBPLATFORM = Str::duplicate(specifics->which_platform);
if (Str::len(INWEBPLATFORM) == 0) {
filename *ps = Filenames::in(path_to_inweb, I"platform-settings.mk");
TextFiles::read(ps, FALSE, "can't open platform settings file",
TRUE, Makefiles::seek_INWEBPLATFORM, NULL, INWEBPLATFORM);
}
2022-04-23 15:13:47 +00:00
if (Str::len(INWEBPLATFORM) == 0) {
Errors::in_text_file(
"found platform settings file, but it does not set INWEBPLATFORM", tfp);
} else {
pathname *P = Pathnames::down(path_to_inweb, I"Materials");
P = Pathnames::down(P, I"platforms");
WRITE_TO(INWEBPLATFORM, ".mkscript");
filename *F = Filenames::in(P, INWEBPLATFORM);
TextFiles::read(F, FALSE, "can't open platform definitions file",
TRUE, Preprocessor::scan_line, NULL, PPS);
WRITE_TO(STDOUT, "(Read definitions file '%S' from ", INWEBPLATFORM);
Pathnames::to_text_relative(STDOUT, path_to_inweb, P);
WRITE_TO(STDOUT, ")\n");
}
}
void Makefiles::seek_INWEBPLATFORM(text_stream *line, text_file_position *tfp, void *X) {
text_stream *OUT = (text_stream *) X;
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, line, L" *INWEBPLATFORM = (%C+) *")) WRITE("%S", mr.exp[0]);
Regexp::dispose_of(&mr);
}
2024-03-09 13:23:53 +00:00
@ \section{The modify filename expander.}
2024-03-09 13:23:53 +00:00
<<*>>=
void Makefiles::modify_filenames_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
text_stream *OUT = PPS->dest;
text_stream *original = parameter_values[0];
text_stream *suffix = parameter_values[1];
text_stream *prefix = parameter_values[2];
wchar_t previous = 'X'; int quoted = FALSE, boundary = FALSE;
TEMPORARY_TEXT(captured)
LOOP_THROUGH_TEXT(pos, original) {
wchar_t c = Str::get(pos);
if (c == '\'') { quoted = quoted?FALSE:TRUE; }
if (Characters::is_whitespace(c)) {
if ((previous != '\\') && (quoted == FALSE)) boundary = TRUE;
} else {
2024-03-09 13:23:53 +00:00
if (boundary) <<Captured a name>>;
boundary = FALSE;
}
PUT_TO(captured, c);
previous = c;
}
2024-03-09 13:23:53 +00:00
<<Captured a name>>
DISCARD_TEXT(captured)
}
2024-03-09 13:23:53 +00:00
<<Captured a name>>=
Str::trim_white_space(captured);
if (Str::len(captured) > 0) {
int in_quotes = FALSE;
if ((Str::get_first_char(captured) == '\'') && (Str::get_last_char(captured) == '\'')) {
Str::delete_first_character(captured);
Str::delete_last_character(captured);
in_quotes = TRUE;
}
if (in_quotes) WRITE("'");
int last_slash = -1;
for (int i=0; i<Str::len(captured); i++)
if (Str::get_at(captured, i) == '/')
last_slash = i;
int last_dot = Str::len(captured);
2022-04-30 16:12:14 +00:00
for (int i=last_slash+1; i<Str::len(captured); i++)
if (Str::get_at(captured, i) == '.')
last_dot = i;
for (int i=0; i<=last_slash; i++) PUT(Str::get_at(captured, i));
WRITE("%S", prefix);
for (int i=last_slash+1; i<last_dot; i++) PUT(Str::get_at(captured, i));
WRITE("%S", suffix);
for (int i=last_dot; i<Str::len(captured); i++) PUT(Str::get_at(captured, i));
if (in_quotes) WRITE("'");
Str::clear(captured);
}
2024-03-09 13:23:53 +00:00
@ \section{The component expander.}
2022-04-23 15:13:47 +00:00
2024-03-09 13:23:53 +00:00
<<*>>=
2022-04-23 15:13:47 +00:00
void Makefiles::component_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
2022-04-23 13:08:38 +00:00
makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics);
text_stream *OUT = PPS->dest;
2022-04-21 23:06:45 +00:00
2022-04-23 13:08:38 +00:00
text_stream *symbol = parameter_values[0];
text_stream *webname = parameter_values[1];
text_stream *path = parameter_values[2];
text_stream *set = parameter_values[3];
text_stream *category = parameter_values[4];
if (Str::eq(category, I"tool")) {
2022-04-23 15:13:47 +00:00
int marker = MAKEFILE_TOOL_MOM;
dictionary *D = specifics->tools_dictionary;
2024-03-09 13:23:53 +00:00
<<Add to dictionary>>;
<<Derive some make symbols>>;
2022-04-23 13:08:38 +00:00
} else if (Str::eq(category, I"web")) {
2022-04-23 15:13:47 +00:00
int marker = MAKEFILE_WEB_MOM;
dictionary *D = specifics->webs_dictionary;
2024-03-09 13:23:53 +00:00
<<Add to dictionary>>;
<<Derive some make symbols>>;
2022-04-23 13:08:38 +00:00
} else if (Str::eq(category, I"module")) {
2022-04-23 15:13:47 +00:00
int marker = MAKEFILE_MODULE_MOM;
dictionary *D = specifics->modules_dictionary;
2024-03-09 13:23:53 +00:00
<<Add to dictionary>>;
<<Derive some make symbols>>;
2022-04-21 23:06:45 +00:00
} else {
2022-04-23 13:08:38 +00:00
Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp);
}
2022-04-23 15:13:47 +00:00
PPS->last_line_was_blank = FALSE;
}
2024-03-09 13:23:53 +00:00
<<Add to dictionary>>=
2022-04-23 15:13:47 +00:00
web_md *Wm = Reader::load_web_md(Pathnames::from_text(path), NULL,
specifics->search_path, TRUE);
Wm->as_module->module_name = Str::duplicate(symbol);
Wm->as_module->module_tag = Str::duplicate(set);
Wm->as_module->origin_marker = marker;
Dictionaries::create(D, symbol);
Dictionaries::write_value(D, symbol, Wm);
2024-03-09 13:23:53 +00:00
<<Derive some make symbols>>=
2022-04-23 15:13:47 +00:00
WRITE("%SLEAF = %S\n", symbol, webname);
WRITE("%SWEB = %S\n", symbol, path);
WRITE("%SMAKER = $(%SWEB)/%S.mk\n", symbol, symbol, webname);
WRITE("%SX = $(%SWEB)/Tangled/%S\n", symbol, symbol, webname);
2024-03-09 13:23:53 +00:00
@ \section{The components loop construct.}
2022-04-23 15:13:47 +00:00
2024-03-09 13:23:53 +00:00
<<*>>=
2022-04-23 15:13:47 +00:00
void Makefiles::components_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
Preprocessor::set_loop_var_name(loop, I"SYMBOL");
text_stream *category = parameter_values[0];
text_stream *set = parameter_values[1];
if (Str::len(set) == 0) set = I"all";
if (Str::eq(category, I"tool")) {
int marker = MAKEFILE_TOOL_MOM;
2024-03-09 13:23:53 +00:00
<<Make the web iterations>>;
2022-04-23 15:13:47 +00:00
} else if (Str::eq(category, I"web")) {
int marker = MAKEFILE_WEB_MOM;
2024-03-09 13:23:53 +00:00
<<Make the web iterations>>;
2022-04-23 15:13:47 +00:00
} else if (Str::eq(category, I"module")) {
int marker = MAKEFILE_MODULE_MOM;
2024-03-09 13:23:53 +00:00
<<Make the web iterations>>;
2022-04-23 15:13:47 +00:00
} else {
Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp);
2022-04-21 23:06:45 +00:00
}
2022-04-22 22:54:19 +00:00
}
2019-02-04 22:26:45 +00:00
2024-03-09 13:23:53 +00:00
<<Make the web iterations>>=
2022-04-23 15:13:47 +00:00
module *M;
LOOP_OVER(M, module) {
if ((M->origin_marker == marker) &&
((Str::eq(set, I"all")) || (Str::eq(set, M->module_tag)))) {
text_stream *value = M->module_name;
Preprocessor::add_loop_iteration(loop, value);
}
}
2024-03-09 13:23:53 +00:00
@ \section{The dependent-files expander.}
2022-04-23 15:13:47 +00:00
2024-03-09 13:23:53 +00:00
<<*>>=
2022-04-23 15:13:47 +00:00
void Makefiles::dependent_files_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
2022-04-23 13:08:38 +00:00
makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics);
text_stream *OUT = PPS->dest;
2019-02-04 22:26:45 +00:00
2022-04-23 13:08:38 +00:00
text_stream *tool = parameter_values[0];
text_stream *modules = parameter_values[1];
text_stream *both = parameter_values[2];
if (Str::len(tool) > 0) {
if (Dictionaries::find(specifics->tools_dictionary, tool)) {
web_md *Wm = Dictionaries::read_value(specifics->tools_dictionary, tool);
Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename);
} else if (Dictionaries::find(specifics->webs_dictionary, tool)) {
web_md *Wm = Dictionaries::read_value(specifics->webs_dictionary, tool);
Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename);
2022-04-22 22:54:19 +00:00
} else {
2022-04-23 13:08:38 +00:00
TEMPORARY_TEXT(erm)
WRITE_TO(erm, "unknown tool '%S' to find dependencies for", tool);
Errors::in_text_file_S(erm, tfp);
DISCARD_TEXT(erm)
2022-04-22 22:54:19 +00:00
}
2022-04-23 13:08:38 +00:00
} else if (Str::len(modules) > 0) {
if (Dictionaries::find(specifics->modules_dictionary, modules)) {
web_md *Wm = Dictionaries::read_value(specifics->modules_dictionary, modules);
Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
} else {
2022-04-22 22:54:19 +00:00
TEMPORARY_TEXT(erm)
2022-04-23 13:08:38 +00:00
WRITE_TO(erm, "unknown module '%S' to find dependencies for", modules);
2022-04-22 22:54:19 +00:00
Errors::in_text_file_S(erm, tfp);
DISCARD_TEXT(erm)
2022-04-23 13:08:38 +00:00
}
} else if (Str::len(both) > 0) {
if (Dictionaries::find(specifics->tools_dictionary, both)) {
web_md *Wm = Dictionaries::read_value(specifics->tools_dictionary, both);
Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
} else if (Dictionaries::find(specifics->webs_dictionary, both)) {
web_md *Wm = Dictionaries::read_value(specifics->webs_dictionary, both);
Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
2022-04-22 22:54:19 +00:00
} else {
2022-04-23 13:08:38 +00:00
TEMPORARY_TEXT(erm)
WRITE_TO(erm, "unknown tool '%S' to find dependencies for", both);
Errors::in_text_file_S(erm, tfp);
DISCARD_TEXT(erm)
}
} else {
2022-04-23 15:13:47 +00:00
Makefiles::pattern(OUT, specifics->for_web->md->sections_md,
specifics->for_web->md->contents_filename);
2022-04-23 13:08:38 +00:00
}
WRITE("\n");
PPS->last_line_was_blank = FALSE;
}
2022-04-23 15:13:47 +00:00
@ This outputs a makefile pattern matching a bunch of web source code filenames:
2024-03-09 13:23:53 +00:00
say, [[inweb/Chapter\ %d/*.w]].
2019-03-10 23:46:11 +00:00
2024-03-09 13:23:53 +00:00
<<*>>=
2020-04-02 12:30:38 +00:00
void Makefiles::pattern(OUTPUT_STREAM, linked_list *L, filename *F) {
dictionary *patterns_done = Dictionaries::new(16, TRUE);
2024-03-09 13:23:53 +00:00
if (F) <<Add pattern for file F, if not already given>>;
2020-04-02 12:30:38 +00:00
section_md *Sm;
LOOP_OVER_LINKED_LIST(Sm, section_md, L) {
filename *F = Sm->source_file_for_section;
2024-03-09 13:23:53 +00:00
<<Add pattern for file F, if not already given>>;
2020-04-02 12:30:38 +00:00
}
}
2024-03-09 13:23:53 +00:00
<<Add pattern for file F, if not already given>>=
2020-04-15 22:45:08 +00:00
pathname *P = Filenames::up(F);
2020-06-27 22:03:14 +00:00
TEMPORARY_TEXT(leaf_pattern)
2020-04-02 12:30:38 +00:00
WRITE_TO(leaf_pattern, "%S", Pathnames::directory_name(P));
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, leaf_pattern, L"Chapter %d*")) {
Str::clear(leaf_pattern); WRITE_TO(leaf_pattern, "Chapter*");
} else if (Regexp::match(&mr, leaf_pattern, L"Appendix %C")) {
Str::clear(leaf_pattern); WRITE_TO(leaf_pattern, "Appendix*");
}
Regexp::dispose_of(&mr);
2020-06-27 22:03:14 +00:00
TEMPORARY_TEXT(tester)
2020-04-02 12:30:38 +00:00
WRITE_TO(tester, "%p/%S/*", Pathnames::up(P), leaf_pattern);
2020-06-27 22:03:14 +00:00
DISCARD_TEXT(leaf_pattern)
2020-04-02 12:30:38 +00:00
Filenames::write_extension(tester, F);
if (Dictionaries::find(patterns_done, tester) == NULL) {
WRITE_TO(Dictionaries::create_text(patterns_done, tester), "got this");
WRITE(" ");
LOOP_THROUGH_TEXT(pos, tester) {
wchar_t c = Str::get(pos);
if (c == ' ') PUT('\\');
PUT(c);
}
}
2020-06-27 22:03:14 +00:00
DISCARD_TEXT(tester)
2020-04-02 12:30:38 +00:00
2022-04-23 15:13:47 +00:00
@ In makefile syntax, spaces must be preceded by slashes in filenames. (That
bald statement really doesn't begin to go into how awkward makefiles can be
when filenames have spaces in, but there we are.)
2022-04-21 23:06:45 +00:00
2024-03-09 13:23:53 +00:00
<<*>>=
2022-04-23 15:13:47 +00:00
void Makefiles::pathname_slashed(OUTPUT_STREAM, pathname *P) {
TEMPORARY_TEXT(PT)
WRITE_TO(PT, "%p", P);
LOOP_THROUGH_TEXT(pos, PT) {
wchar_t c = Str::get(pos);
if (c == ' ') WRITE("\\ ");
else PUT(c);
}
DISCARD_TEXT(PT)
2022-04-21 23:06:45 +00:00
}