# HG changeset patch # User Paul Boddie # Date 1670632102 -3600 # Node ID 170967cc0f8bf1574d08120d7aff974e7ddb0e88 # Parent 7fa8fbaae4c41a40ac66091e1d5345a1f7d236e8 Introduced support for the definition of compound interfaces, removing the command options that have been provided to achieve similar results. Updated the L4Re build system support to work with the revised compound interface functionality. Also introduced measures to prevent the idl command from being run many times where a collection of output files depends on many input files (this being a known annoyance with Makefiles). Added examples of compound interface definitions. Introduced an import statement to the interface definition syntax, changing input file processing to permit the parsing of multiple files and the analysis of parsed interfaces to resolve base interface references. Fixed the generation of C language object state references supporting compound interfaces in server code. Changed the Makefile to make C files dependent on header files. Updated and expanded the documentation. diff -r 7fa8fbaae4c4 -r 170967cc0f8b Makefile --- a/Makefile Fri Dec 09 19:33:15 2022 +0100 +++ b/Makefile Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # Makefile for building the idl tool. # -# Copyright (C) 2019, 2020, 2021 Paul Boddie +# Copyright (C) 2019, 2020, 2021, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -17,11 +17,18 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA -# Original sources and final program. +# Original sources, headers and final program. SOURCES = \ - main.c client.c common.c config.c declaration.c dispatch.c includes.c \ - interface.c message.c program.c server.c structure.c summary.c \ + main.c client.c common.c config.c declaration.c dispatch.c imports.c \ + includes.c interface.c message.c program.c server.c \ + structure.c summary.c + +HEADERS = \ + client.h common.h config.h declaration.h dispatch.h imports.h \ + includes.h interface.h message.h parser.h program.h server.h \ + structure.h summary.h \ + templates.h types.h PROGRAM = idl @@ -71,5 +78,5 @@ main.c: $(VERSION) -.c.o: +%.o: %.c $(HEADERS) $(CC) $(CFLAGS) -o $@ -c $< diff -r 7fa8fbaae4c4 -r 170967cc0f8b client.c --- a/client.c Fri Dec 09 19:33:15 2022 +0100 +++ b/client.c Sat Dec 10 01:28:22 2022 +0100 @@ -165,16 +165,21 @@ struct signature *sig; char *opname; - fprintf(fp, client_interface_prologue_c, iface->name, iface->name); - - for (sig = iface->signatures; sig != NULL; sig = sig->tail) + if (iface->signatures == NULL) + fprintf(fp, client_interface_prologue_empty_c, iface->name, iface->name); + else { - opname = get_operation_name(iface, sig); + fprintf(fp, client_interface_prologue_c, iface->name, iface->name); - fprintf(fp, client_interface_member_c, sig->operation, opname); + for (sig = iface->signatures; sig != NULL; sig = sig->tail) + { + opname = get_operation_name(iface, sig); - free(opname); + fprintf(fp, client_interface_member_c, sig->operation, opname); + + free(opname); + } + + fputs(client_interface_epilogue_c, fp); } - - fputs(client_interface_epilogue_c, fp); } diff -r 7fa8fbaae4c4 -r 170967cc0f8b common.c --- a/common.c Fri Dec 09 19:33:15 2022 +0100 +++ b/common.c Sat Dec 10 01:28:22 2022 +0100 @@ -34,6 +34,13 @@ fprintf(stderr, "Could not open source file for writing in directory %s (name %s).\n", dir, name); } +/* Test for a basename. */ + +int is_basename(char *path) +{ + return basename(path) == path; +} + /* Obtain a new basename string. */ char *make_basename(const char *path) @@ -77,6 +84,13 @@ return fp; } +/* Return whether an interface is a compound interface. */ + +int is_compound_interface(struct interface *iface) +{ + return iface->bases != NULL; +} + /* Find an attribute value. */ char *get_attribute_value(struct attribute *attr, const char *name) @@ -352,12 +366,26 @@ /* Return the maximum number of input items accepted by any one of the given signatures. */ -int get_max_input_items(struct signature *sig) +int get_max_input_items(struct interface *iface) { int input_words, input_items, output_words, output_items; int max_input_items = 0; + struct interface_ref *base; + struct signature *sig; - for (; sig != NULL; sig = sig->tail) + /* Test any base interfaces. */ + + for (base = iface->bases; base != NULL; base = base->tail) + { + input_items = get_max_input_items(base->iface); + + if (input_items > max_input_items) + max_input_items = input_items; + } + + /* Test the signatures. */ + + for (sig = iface->signatures; sig != NULL; sig = sig->tail) { count_parameters(sig->parameters, &input_words, &input_items, &output_words, &output_items); diff -r 7fa8fbaae4c4 -r 170967cc0f8b common.h --- a/common.h Fri Dec 09 19:33:15 2022 +0100 +++ b/common.h Sat Dec 10 01:28:22 2022 +0100 @@ -28,8 +28,9 @@ void open_error(const char *dir, const char *name); -/* Filename manipulation. */ +/* Filename testing and manipulation. */ +int is_basename(char *path); char *make_basename(const char *path); char *make_dirname(const char *path); @@ -37,6 +38,10 @@ FILE *get_output_file(const char *filename_template, const char *dir, const char *name); +/* Interface testing. */ + +int is_compound_interface(struct interface *iface); + /* Interface attribute access. */ char *get_attribute_value(struct attribute *attr, const char *name); @@ -80,7 +85,7 @@ void count_parameters(struct parameter *param, int *input_words, int *input_items, int *output_words, int *output_items); -int get_max_input_items(struct signature *sig); +int get_max_input_items(struct interface *iface); /* Message access details. */ diff -r 7fa8fbaae4c4 -r 170967cc0f8b config.c --- a/config.c Fri Dec 09 19:33:15 2022 +0100 +++ b/config.c Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Configuration settings. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,10 +23,9 @@ #include "config.h" struct config conf = { - .output_prefix = NULL, .output_dir = NULL, - .compound = NULL, - .compound_name = NULL, + .generate_all = 0, + .show_filenames = 0, .client = 0, .server = 0, .headers = 0, diff -r 7fa8fbaae4c4 -r 170967cc0f8b config.h --- a/config.h Fri Dec 09 19:33:15 2022 +0100 +++ b/config.h Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Configuration settings. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -25,15 +25,11 @@ struct config { - /* Current filename and output options. */ - - char *output_prefix; - char *output_dir; + /* Current output options. */ - /* Compound interface and dispatcher output. */ - - char *compound; - char *compound_name; + char *output_dir; + int generate_all; + int show_filenames; /* Configurations for generated code. */ diff -r 7fa8fbaae4c4 -r 170967cc0f8b dispatch.c --- a/dispatch.c Fri Dec 09 19:33:15 2022 +0100 +++ b/dispatch.c Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Generation of server dispatch and handle functions. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,6 +26,93 @@ +/* Generate each dispatch possibility within an interface. */ + +static void write_dispatcher_cases(FILE *fp, struct interface *iface, + int compound) +{ + struct signature *sig; + char *opcode, *opname, *protocol, *prefix, *ref, *s; + + for (sig = iface->signatures; sig != NULL; sig = sig->tail) + { + opname = get_operation_name(iface, sig); + opcode = get_opcode_identifier(NULL, opname); + prefix = get_operation_wrapper_prefix(sig->attributes); + + /* Convert to any base interface. */ + + ref = get_object_conversion(iface, compound); + + /* Generate a reply if appropriate. */ + + if (have_attribute(sig->attributes, "completion")) + s = dispatch_function_wrapper_case; + else + s = dispatch_function_reply_wrapper_case; + + /* Generate the case and invocation. */ + + fprintf(fp, s, opcode, prefix, opname, ref); + + /* Free allocated strings. */ + + free(opcode); + free(opname); + free(ref); + } +} + +/* Dispatch to each interface-level function where the same protocol applies to + an entire interface. Dispatch to each operation where an interface does not + specify a protocol. */ + +static void write_dispatcher_interface_cases(FILE *fp, struct interface *iface, + int by_protocol) +{ + struct interface_ref *base; + char *protocol, *ref; + + for (base = iface->bases; base != NULL; base = base->tail) + { + protocol = get_protocol(base->iface->attributes); + + /* Write cases for each interface, using the protocol to select between the + cases. */ + + if (by_protocol && (protocol != NULL)) + { + /* Convert to any base interface. */ + + ref = get_object_conversion(base->iface, 1); + fprintf(fp, dispatch_function_interface_case, protocol, base->name, ref); + free(ref); + } + + /* Write cases for each operation provided by the interface so that they can + be selected. Note that this requires distinct opcodes to be used, so in + practice requiring explicit opcodes to be indicated. */ + + else if (!by_protocol && (protocol == NULL)) + write_dispatcher_cases(fp, base->iface, 1); + } +} + +/* Return whether any base interface specifies a protocol. */ + +static int have_interfaces_using_protocols(struct interface *iface) +{ + struct interface_ref *base; + + for (base = iface->bases; base != NULL; base = base->tail) + if (get_protocol(base->iface->attributes) != NULL) + return 1; + + return 0; +} + + + /* Generate dispatch function signatures. */ void write_dispatcher_signature(const char *name, enum signature_role role, @@ -46,61 +133,47 @@ /* Generate a dispatch function for the different operations. */ -void write_dispatcher(struct signature *sig, FILE *fp, struct interface *iface) +void write_dispatcher(FILE *fp, struct interface *iface) { + char *protocol = get_protocol(iface->attributes); + write_dispatcher_signature(iface->name, DEFINITION_ROLE, fp); /* Declare an error variable to support testing for already-sent messages. */ fputs(" long err;\n\n", fp); - /* Interpret an operation indicator in the word data if a protocol applies to - the entire interface. */ + /* Without a protocol applying to the entire interface, dispatch using the + protocol from the message label. */ - if (get_protocol(iface->attributes)) - fputs(" switch (ipc_message_get_word(msg, 0))\n {\n", fp); - else + if (protocol == NULL) + { fputs(" switch (l4_msgtag_label(msg->tag))\n {\n", fp); - write_dispatcher_cases(sig, fp, iface, 0); - fputs(server_function_dispatcher_body_epilogue, fp); + /* Dispatch using the protocol to base interfaces employing protocols. */ - fputs("}\n", fp); -} - -/* Generate each dispatch possibility within an interface. */ + if (have_interfaces_using_protocols(iface)) + write_dispatcher_interface_cases(fp, iface, 1); + } -void write_dispatcher_cases(struct signature *sig, FILE *fp, - struct interface *iface, int compound) -{ - char *opcode, *opname, *protocol, *prefix, *ref, *s; + /* If a protocol applies to the entire interface, dispatch using an operation + indicator in the word data. */ - if (sig == NULL) - return; + else + fputs(" switch (ipc_message_get_word(msg, 0))\n {\n", fp); - opname = get_operation_name(iface, sig); - opcode = get_opcode_identifier(NULL, opname); - prefix = get_operation_wrapper_prefix(sig->attributes); - ref = get_object_conversion(iface, compound); + /* Dispatch to operations provided by compound interfaces. */ - /* Generate a reply if appropriate. */ + write_dispatcher_interface_cases(fp, iface, 0); - if (have_attribute(sig->attributes, "completion")) - s = dispatch_function_wrapper_case; - else - s = dispatch_function_reply_wrapper_case; + /* Dispatch to each operation defined at this level. */ - /* Generate the case and invocation. */ - - fprintf(fp, s, opcode, prefix, opname, ref); + write_dispatcher_cases(fp, iface, is_compound_interface(iface)); - /* Generate the other cases. */ - - write_dispatcher_cases(sig->tail, fp, iface, compound); + /* Terminate the dispatcher. */ - /* Free allocated strings. */ + fputs(dispatch_function_default_case, fp); + fputs(dispatch_function_dispatcher_epilogue, fp); - free(opcode); - free(opname); - free(ref); + fputs(END_FUNCTION, fp); } diff -r 7fa8fbaae4c4 -r 170967cc0f8b dispatch.h --- a/dispatch.h Fri Dec 09 19:33:15 2022 +0100 +++ b/dispatch.h Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Generation of server dispatch and handle functions. * - * Copyright (C) 2019, 2020 Paul Boddie + * Copyright (C) 2019, 2020, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -24,8 +24,6 @@ #include #include "types.h" -void write_dispatcher(struct signature *sig, FILE *fp, struct interface *iface); -void write_dispatcher_cases(struct signature *sig, FILE *fp, - struct interface *iface, int compound); +void write_dispatcher(FILE *fp, struct interface *iface); void write_dispatcher_signature(const char *name, enum signature_role role, FILE *fp); void write_handler_signature(const char *name, enum signature_role role, FILE *fp); diff -r 7fa8fbaae4c4 -r 170967cc0f8b docs/idl.1 --- a/docs/idl.1 Fri Dec 09 19:33:15 2022 +0100 +++ b/docs/idl.1 Sat Dec 10 01:28:22 2022 +0100 @@ -1,4 +1,4 @@ -.TH IDL "1" "2022-09-15" "idl 0.1" "User Commands" +.TH IDL "1" "2022-12-09" "idl 2022-12-09" "User Commands" .SH NAME idl \- L4Re IDL parser and code generator @@ -60,6 +60,10 @@ .BR \-c ", " \-\-client Generate client files only, appropriate when needing to access interfaces. .TP +.BR \-f ", " \-\-files +Only show processed filenames on standard output, with no output files being +produced. +.TP .BR \-h ", " \-\-headers Generate header files for the selected configuration or for both client and server configurations. @@ -69,6 +73,11 @@ implementing the operations of the described interfaces. Common definitions employed by client and server code are also defined in these files. .TP +.BR \-R ", " \-\-recursive +Generate output files corresponding to all interface files processed. Where an +interface file imports other files, output will also be generated for these +files. +.TP .BR \-r ", " \-\-routines Generate routines, these defining functions exposing the operations of the described interfaces. @@ -86,20 +95,16 @@ configuration has been selected. .PP If the -.BR \-c ", " \-\-client ", " \-s ", " \-\-server ", " \-C ", " \-\-comp ", " -.BR \-N " or " \-\-comp-name +.BR \-c ", " \-\-client ", " \-s " or " \-\-server options are not given, output is produced for client and server configurations. +The +.BR \-f " and " \-\-files +options will suppress all output regardless of any other options indicated. .PP Some options may be followed by a value, either immediately after the long form of the option and an equals sign (\fB=\fR) or in the argument that follows them: .PP .TP -\fB\-C\fR, \fB\-\-comp\fR=\fIPREFIX\fR -Generate compound (or composite, or component) interface files, indicating the -name of the compound interface which will also be the prefix of the generated -files; such files are employed by servers to pass control to individual -interfaces. -.TP \fB\-d\fR, \fB\-\-dir\fR=\fIDIRECTORY\fR Indicate the output directory for generated files. .TP @@ -107,12 +112,6 @@ Select the programming language to be used for output; currently only .BR C " and " C++ are accepted as values for this option. -.TP -\fB\-N\fR, \fB\-\-comp-name\fR=\fINAME\fR -Select a different name for a compound (or composite, component) interface than -that indicated by the -.BR \-C " or " \-\-comp -options. .SH EXAMPLES Compile the interfaces in @@ -143,20 +142,13 @@ .IP idl --language=C++ -r -s hello.idl .PP -Generate compound interface header files and dispatch routines for a server: +Generate files for multiple interface files: .IP -idl -C greetings hello.idl goodbye.idl +idl hello.idl goodbye.idl .PP -The above would create a dispatch mechanism for a component called -.I greetings -supporting both of the indicated interfaces. This complements server code -generated for the individual interfaces: +Generate all output files for an interface file that imports other files: .IP -idl -s hello.idl goodbye.idl -.PP -Generate just the interface headers for the individual interfaces: -.IP -idl -i hello.idl goodbye.idl +idl -R hello_and_goodbye.idl .SH INTERFACE DESCRIPTIONS An interface will resemble the following in its simplest practical form: @@ -250,11 +242,14 @@ \fBopcode_type(\fIopcode_type_value\fB)\fR An explicit type for the opcode applying to the given operation. -.SH COMPLETION OPERATIONS -Operations can be annotated with the \fBcompletion\fR attribute, indicating -that a client will initiate the operation with any parameters conveying input -values, with the completion of the operation being performed explicitly by -server code. +.SH SENDING RESULTS DURING OPERATIONS +.PP +Normally, results are communicated from operations by setting the appropriate +output parameter values. However, it is possible to send results in advance by +calling a completion function. Completion functions are generally made available +for all operations, regardless of whether the \fBcompletion\fR attribute is +specified. Such default completion functions merely provide a way of +communicating results. .PP Consider the following interface: .in +4n @@ -274,10 +269,56 @@ Calc_pow(ref_Calc _self, double left, double right, double *result); .fi .PP -Here, the result is written to the address provided by the corresponding -parameter. When the client invokes the operation, it remains unaware of the -nature of the work done by the server: the operation acts like a typical -function call. +Here, the result is normally written to the address provided by the +corresponding parameter. However, the operation is also supported by another +function as follows: +.in +4n +.nf +.sp +complete_Calc_pow(double result); +.fi +.PP +Instead of using the \fBresult\fR output parameter in \fBCalc_pow\fR to return a +result value, this parameter can be ignored and the \fBcomplete_Calc_pow\fR +function used to send a response to the client, the result value being passed as +a parameter to this function. To prevent the server framework from attempting to +use the \fBresult\fR output parameter to send another response, a special value +is returned from the operation: +.in +4n +.nf +.sp +/* Complete the operation and send results. */ + +complete_Calc_pow(result); + +/* More work done. */ + +return IPC_MESSAGE_SENT; +.fi +.PP +Unlike explicitly designated completion operations, results sent in this way +will be directed to the initiating client as a reply to its original request. + +.SH COMPLETION OPERATIONS +Operations can be annotated with the \fBcompletion\fR attribute, indicating +that a client will initiate the operation with any parameters conveying input +values, with the completion of the operation being performed explicitly by +server code. Unlike regular operations, such annotated operations allow the +completion to involve another endpoint, as opposed to the client that initiated +the operation. +.PP +Consider the following interface: +.in +4n +.nf +.sp +interface Calc +{ + void pow(in double left, in double right, out double result); +}; +.fi +.PP +This interface would be presented to the client for it to use when initiating +operations. .PP Now consider the following interface annotated with the \fBcompletion\fR attribute: @@ -297,8 +338,9 @@ CalcPrivate_pow(ref_CalcPrivate _self, double base, double exponent); .fi .PP -This function is not intended to communicate the result. Instead, within the -server code, a function of the following form will need to be invoked: +This function is not intended to communicate the result, and it even lacks the +appropriate output parameter. Instead, within the server code, a function of the +following form will need to be invoked: .in +4n .nf .sp @@ -306,23 +348,52 @@ .fi .PP Here, the result is communicated via the corresponding parameter, with the -indicated endpoint (\fB_endp\fR) receiving the reply. +indicated endpoint (\fB_endp\fR) receiving the reply. Where the endpoint is +the client that initiated an operation, the effect will be indistinguishable +from a normal invocation. However, where another endpoint is chosen to receive +the reply, the initiating client will effectively give up control to the client +employing the chosen endpoint, with that designated client assuming control +until such time that it calls the operation. Thus, this form of completion +operation permits a form of simple scheduling. .SH COMPONENTS AND COMPOUND INTERFACES -Components may attempt to support more than one interface. Although inheritance -might be employed to achieve this, the combination or composition of interfaces -is somewhat different: crucially, all involved interfaces remain distinct and do -not override each other (conflicts between protocols and operation codes -notwithstanding). +Components may wish to support more than one interface, and this can be done +using composition, as illustrated below involving two interfaces: +.in +4n +.nf +.sp +interface \fIinterface_name\fR composes \fIbase_interface1\fR, \fIbase_interface2\fR; +.fi +.PP +Although inheritance might be employed to provide compound interfaces, the +combination or composition of interfaces is somewhat different: crucially, all +involved interfaces remain distinct and do not override each other (conflicts +between protocols and operation codes notwithstanding). +.PP +Base interfaces, these being interfaces used in composition to make a compound +interface, can be defined in the same file as the compound interface. +Alternatively, an \fBimport\fR statement can be used to import interface +definitions from another file, such files residing in the same directory as the +file currently being processed. For example: +.in +4n +.nf +.sp +import "calc.idl"; +import "counter.idl"; + +interface \fICalcCounter\fR composes \fICalc\fR, \fICounter\fR; +.fi +.PP +Here, \fBcalc.idl\fR would provide \fBCalc\fR, and \fBcounter.idl\fR would +provide \fBCounter\fR. .SH FILES .B idl reads all interfaces in a single input file and generates output files that -each contain the details of all of these interfaces. Apart from the compound -interface files, output files are written alongside their corresponding input -files. This behaviour can be overridden with the \fB\-d\fR or \fB\-\-dir\fR -options, choosing a specific output directory for all files including compound -interface files. +each contain the details of all of these interfaces. By default, output files +are written alongside their corresponding input files, but this behaviour can be +overridden with the \fB\-d\fR or \fB\-\-dir\fR options, choosing a specific +output directory for all files including compound interface files. .PP The following kinds of output files are generated for a file with a name of the form @@ -343,7 +414,9 @@ .TP .IR name "_server.c or " name _server.cc contains the function definitions needed to provide entry points for the -operations of all the interfaces. +operations of all the interfaces. Where an interface is composed of other +interfaces, the function definitions will dispatch to these interfaces' own +functions. .TP .IR name _server.h contains the function declarations, signatures or prototypes for the entry @@ -354,29 +427,11 @@ processes each one in turn, generating a separate set of output files for each input file. .PP -For server components, compound interface files can also be produced with their -names chosen using the \fB\-C\fR or \fB\-\-comp\fR options. With a value of -\fIname\fR given with these options, files of the following form are produced: -.TP -.IR name _interface.h -contains a compound interface declaration (for C++ only) needed for the dispatch -mechanism to function, also to be used by the actual component implementing the -interfaces. -.TP -.IR name _interfaces.h -contains include statements referencing individual interfaces, needed by the -above compound interface declaration. -.TP -.IR name "_server.c or " name _server.cc -contains the function definitions needed to dispatch to the interfaces. -.TP -.IR name _server.h -contains the function declarations, signatures or prototypes for the dispatch -functions. -.PP -These files provide support for interpreting messages received by servers and -directing handling of such messages to interface-specific dispatch functions or -directly to functions acting as operation entry points. +Output files will only be produced corresponding to the indicated input files, +even if those files import other files. To generate output for the input files +together with the imported files, use the +.BR \-R " or " \-\-recursive +options. .SH L4RE BUILD SYSTEM INTEGRATION To be useful in the L4Re build system, the @@ -384,10 +439,10 @@ distribution should be made available with the name .I idl4re in the -.I src/l4 -section of the general L4Re distribution. Rules can then be added to Makefiles -to invoke the tool and to produce source files that can then be compiled in the -normal way. A collection of Makefile-compatible files are provided in the +.IR l4 " section (or " src/l4 " in earlier forms)" +of the general L4Re distribution. Rules can then be added to Makefiles to invoke +the tool and to produce source files that can then be compiled in the normal +way. A collection of Makefile-compatible files are provided in the .I mk directory within the .I idl4re @@ -402,12 +457,19 @@ error details from the scanner and parser. Obviously, this should be improved to make the tool more usable. .PP +Memory management is not comprehensive. However, this tool is meant to be run +for the duration of a given code generation task, not run as a service +indefinitely. +.PP Validation of input is also minimal, with the tool mostly acting as a kind of preprocessor, generating code that may then not behave exactly as intended, although the code should be well-formed. .PP -Interface inheritance is not supported, neither are modules. Both of these -things are typically available in other languages describing interfaces. +File inclusion is currently limited to loading files that all reside in the same +location, although include paths could be supported. +.PP +Things like modules and type definitions are not supported, these typically +being available in other languages describing interfaces. .SH AUTHOR Paul Boddie diff -r 7fa8fbaae4c4 -r 170967cc0f8b docs/wiki/Clients --- a/docs/wiki/Clients Fri Dec 09 19:33:15 2022 +0100 +++ b/docs/wiki/Clients Sat Dec 10 01:28:22 2022 +0100 @@ -4,7 +4,8 @@ those components' interfaces, potentially via interprocess communications mechanisms. Given an interface description featuring component operations, code may be written to invoke these operations and to treat the component as -if it resides in the same program. +if it resides in the same program, even if it actually resides in another +program operating as a [[Servers|server]]. <> diff -r 7fa8fbaae4c4 -r 170967cc0f8b docs/wiki/Development --- a/docs/wiki/Development Fri Dec 09 19:33:15 2022 +0100 +++ b/docs/wiki/Development Sat Dec 10 01:28:22 2022 +0100 @@ -18,11 +18,6 @@ * Input file access and output coordination. - * Parser initialisation and invocation using the `yyrestart` and - `yyparse` functions, and parser state management. - -Various helper functions are also defined. - == Configuration == The `config.c` and `config.h` files define configuration state for the program @@ -97,10 +92,11 @@ directly by the scanner. For example, the `include` rule obtains a value associated with a header -filename. This filename is associated with the `HEADER` token and its value is -interpreted as a string. However, the rule needs to prepare a structure that -incorporates the filename and that can be referenced by other structures. To -achieve this, a `%union` member is defined for the structure type concerned: +filename. This filename is associated with the `PHEADER` and `QHEADER` tokens +and its value is interpreted as a string. However, the rule needs to prepare a +structure that incorporates the filename and that can be referenced by other +structures. To achieve this, a `%union` member is defined for the structure +type concerned: {{{ %union { @@ -220,56 +216,63 @@ { node [shape=record,fontsize="12.0",fontname="sans-serif"]; - interface [label="{interface | {name | signatures | attributes | includes | tail}}"]; - signature [label="{signature | {qualifier | operation |

parameters | attributes | tail}}"]; - attribute [label="{attribute | {attribute | identifiers | tail}}"]; - parameter [label="{parameter | {specifier | class | identifiers | tail}}"]; - identifier [label="{identifier | {identifier | tail}}"]; - include [label="{include | {filename | tail}}"]; + interface [label="{ interface | {name | bases | signatures | attributes | imports | includes | tail}}"]; + interface_ref [label="{ interface_ref | {name | iface | tail}}"]; + signature [label="{ signature | {qualifier | operation |

parameters | attributes | tail}}"]; + attribute [label="{ attribute | {attribute | identifiers | tail}}"]; + parameter [label="{ parameter | {specifier | class | identifiers | tail}}"]; + identifier [label="{ identifier | {identifier | tail}}"]; + import [label="{ import | {filename | tail}}"]; + include [label="{ include | {filename | tail}}"]; + interface:b -> interface_ref; interface:s -> signature; interface:a -> attribute; + interface:I -> import; interface:i -> include; - interface:t -> interface; + interface:t -> interface:X; + + interface_ref:i -> interface:X; + interface_ref:t -> interface_ref:X; signature:p -> parameter; signature:a -> attribute; - signature:t -> signature; + signature:t -> signature:X; attribute:i -> identifier; - attribute:t -> attribute; + attribute:t -> attribute:X; parameter:i -> identifier; - parameter:t -> parameter; + parameter:t -> parameter:X; + + identifier:t -> identifier:X; - identifier:t -> identifier; + import:t -> import:X; - include:t -> include; + include:t -> include:X; } }}} ######## End of graph. The nature of the hierarchy should reflect the conceptual form of the input. -It should be noted that header file information (represented by `include` -structures) is associated with interface information (represented by -`interface` structures). This arrangement merely attempts to indicate the -header file declarations that preceded specific interface declarations, but -the two different types of information should arguably be grouped within a +It should be noted that imported file and header file information (represented +by `import` and `include` structures respectively) is associated with +interface information (represented by `interface` structures). This +arrangement merely attempts to indicate the import and header file +declarations that preceded specific interface declarations, but the two +different types of information should arguably be grouped within a file-oriented structure. == Code Generation == The `program.c` and `program.h` files define the functions that coordinate the generation of program code. It is in `program.c` that files are opened for -writing (using the `get_output_file` function provided by `common.c`), and two -principal functions are involved in initiating the population of these files: - - * `begin_compound_output` - * `write_files` - -These functions are described in more detail with regard to the topics of -compound and individual interfaces. +writing (using the `get_output_file` function provided by `common.c`), and the +principal function involved in initiating the population of these files is the +`write_files` function. This function then invokes `write_interfaces` to +coordinate the code generation for each of the interfaces described by the +input. Meanwhile, a selection of writer functions are employed to generate code for the different structures employed within interface descriptions. Since output @@ -289,93 +292,21 @@ Through the use of roles, the same fundamental information can be expressed in different ways by the same routine. -=== Compound Interfaces === - -The `begin_compound_output` function is called by the main program when -compound interface generation has been requested. It produces extra output -that references and augments output produced for individual interfaces. +=== Interfaces === -Various details of individual interfaces are incorporated into the compound -interface output. To achieve this, once the `begin_compound_output` function -has been called, individual interface output is generated. During this -activity, the `write_compound_output` function is called for each individual -interface to insert details of that interface into the appropriate place -within the compound interface output. - -The `end_compound_output` function ultimately closes the files involved, -either through being invoked by the main program or upon a failure condition. - -The following diagram summarises the general function organisation involved. +The `write_files` function coordinates output generation for each interface. ######## A graph showing the function organisation involved in generating -######## compound interfaces... +######## interfaces... {{{#!graphviz #format svg #transform notugly -digraph compound +digraph interfaces { node [shape=box,fontsize="12.0",fontname="sans-serif",style=filled,fillcolor=white]; rankdir=LR; - parser [shape=ellipse]; - - subgraph { - rank=same; - server [shape=folder,fillcolor="#77ff77",label="..._server.{c,cc,h}"]; - interface [shape=folder,fillcolor="#77ff77",label="..._interface.h"]; - interfaces [shape=folder,fillcolor="#77ff77",label="..._interfaces.h"]; - interface_type [shape=folder,fillcolor="#77ff77",label="..._interface_type.h"]; - } - - subgraph { - rank=same; - main -> yyparse -> parser -> write_files -> write_interfaces; - } - - main -> begin_compound_output; - main -> end_compound_output; - - begin_compound_output -> write_handler_signature; - begin_compound_output -> write_dispatcher_signature; - - write_handler_signature -> server; - - write_dispatcher_signature -> server; - - write_files -> write_compound_dispatch_include -> server; - - write_interfaces -> write_compound_output; - - write_compound_output -> write_dispatcher_cases -> server; - write_compound_output -> write_compound_interface; - - write_compound_interface -> interface; - write_compound_output -> write_include -> interfaces; - write_include -> interface_type; -} -}}} - -######## End of graph. - -=== Individual Interfaces === - -The `write_files` function coordinates the generation of individual interface -output. - -######## A graph showing the function organisation involved in generating -######## individual interfaces... - -{{{#!graphviz -#format svg -#transform notugly -digraph individual -{ - node [shape=box,fontsize="12.0",fontname="sans-serif",style=filled,fillcolor=white]; - rankdir=LR; - - parser [shape=ellipse]; - subgraph { rank=same; client [shape=folder,fillcolor="#77ff77",label="..._client.{c,cc,h}"]; @@ -385,7 +316,7 @@ subgraph { rank=same; - main -> yyparse -> parser -> write_files -> write_interfaces; + main -> import_file -> write_files -> write_interfaces; } write_interfaces -> write_client_interface; @@ -395,6 +326,7 @@ write_interfaces -> write_handler_signature; write_interfaces -> write_include; write_interfaces -> write_interface_definition; + write_interfaces -> write_server_includes -> write_include; write_interfaces -> write_signatures; write_client_interface -> client; @@ -415,19 +347,67 @@ ######## End of graph. +=== Imports and Parsing === + +The `imports.c` file coordinates the parsing and code generation activities. +Upon running the tool, the `import_file` function is invoked by the `main` +function (in `main.c`) for each input file. + +The `import_file` function invokes the parser and with a successfully parsed +result, it then seeks to resolve any `import` statements mentioned in the +input. The `resolve_imports` function iterates over any such imports, these +represented by import structures, calling the `import_file` function to obtain +another parsing result structure for each import, with a reference to this +structure being stored in the import structure concerned, thus establishing +inter-file relationships. Such importing is done recursively. + +######## A graph showing the function organisation involved in importing +######## files... + +{{{#!graphviz +#format svg +#transform notugly +digraph importing +{ + node [shape=box,fontsize="12.0",fontname="sans-serif",style=filled,fillcolor=white]; + rankdir=LR; + + parser [shape=ellipse]; + + subgraph { + rank=same; + + main -> import_file; + } + + import_file -> yyparse -> parser; + import_file -> resolve_imports -> import_file; + import_file -> populate_bases; +} +}}} + +######## End of graph. + +Where compound interfaces are defined in the input, these composing or +combining other interfaces, the identity of such base interfaces needs to be +determined. This is done by the `populate_bases` function which searches the +parsing results from the location of each compound interface whose base +interfaces are to be resolved, traversing interface definitions and imports. +When determined, an interface definition will then be directly referenced by +the base interface reference associated with the compound interface. + === Includes and Headers === -The `includes.c` and `includes.h` files provide support for writing `#include` -statements in C and C++ programs, with the `write_includes` function -traversing an `include` structure list to emit a list of statements. +The `includes.c` file provides support for writing `#include` statements in C +and C++ programs, with the `write_includes` function traversing an `include` +structure list to emit a list of statements. === Interface Definitions === -The `interface.c` and `interface.h` files provide the -`write_interface_definition` function which is concerned with generating a -description of an interface in two different contexts: client and server. -Some generated files are employed in both contexts such as the file of the -form `_interface.h`. +The `interface.c` file provides the `write_interface_definition` function +which is concerned with generating a description of an interface in two +different contexts: client and server. Some generated files are employed in +both contexts such as the file of the form `_interface.h`. ==== Client Definitions ==== @@ -464,25 +444,64 @@ === Templates and Output === - * `templates.h` +The strings used to generate output are provided in `templates.h`. Many of +these strings contain placeholders in the form of output specifiers such as +`%s` and `%d` used by the `fprintf` function. A more sophisticated approach +would involve the use of a template language, which would potentially simplify +this tool's code and make the output generation mechanisms somewhat clearer. + +=== Clients === + +The `client.c` file is concerned with generating wrapper functions that +present the operations exposed by an interface to a client program. These +wrapper functions take any parameters supplied to them and populate a message +to be sent to the server providing the implementation of the interface, +sending the message to the server, interpreting the reply, and setting output +parameters appropriately. + +The form of client wrapper functions is similar to that of server wrapper +functions, and various common functions are used by both client and server +code generation activities. === Servers === - * `server.c` +The `server.c` file is concerned with the generation of code for wrapper +functions for each of the operations associated with an interface, this code +obtaining values from messages and invoking the actual operation +implementations. + +Most of the functions provided are concerned with the generation of variable +declarations and the initialisation of such variables from messages, the +formulation of invocation statements, and the population of messages from +operation results. Various common functions are used by client and server code +generation activities. === Dispatchers and Handlers === - * `dispatch.c` +The generation of code that interprets message details and dispatches to the +wrapper functions written by `server.c` can be found in the `dispatch.c` file. === Parameters and Members === - * `declaration.c` +The `declaration.c` file provides general support for generating declarations +for client and server code, handling artefacts such as interfaces, function +signatures and parameters. === Message Structures and Access === - * `message.c` - * `structure.c` +The `message.c` file provides support for the generation of statements related +to the access of message contents. The functions provided are used by the +`client.c` and `server.c` files in the generation of wrapper functions. + +The `structure.c` file concerns itself with the generation of operation code +(opcode) enumerations, these giving each operation a distinct identifier, +along with the generation of structures used to interpret and populate +messages for each operation. === Summaries === - * `summary.c` +The `summary.c` file provides functions to display the structure of parsed +interface files. Each function corresponds to a data structure defined in +`types.h`, with references traversed to other structures. The `show_interface` +function is the entry point from which other structures created during parsing +are reached. diff -r 7fa8fbaae4c4 -r 170967cc0f8b docs/wiki/L4Re_Support --- a/docs/wiki/L4Re_Support Fri Dec 09 19:33:15 2022 +0100 +++ b/docs/wiki/L4Re_Support Sat Dec 10 01:28:22 2022 +0100 @@ -316,84 +316,3 @@ The variables are then employed in filename transformations, described in more detail elsewhere in this document. - -=== Compound Interfaces === - -The `idl` tool also supports compound interfaces which combine individual -interface descriptions so that server components can expose multiple -interfaces. Here, a naming convention is employed: - -|| '''Variable''' || '''Details''' || -|| `interface_NAME` || Indicates the program name of a compound object || -|| `interface_INTERFACES` || A list of individual interface descriptions || - -The `interface` portion of each variable is replaced by a compound interface -name as in the following example: - -{{{ -mapped_file_object_NAME = MappedFileObject -mapped_file_object_INTERFACES = dataspace file mapped_file sync -}}} - -Thus, the compound interface description `mapped_file_object` employs the -stated individual interfaces and program name: - -######## A diagram showing an example compound interface... - -{{{#!graphviz -#format svg -#transform notugly -digraph example -{ - graph [splines=ortho]; - node [shape=box,fontsize="12.0",fontname="sans-serif",style=filled,fillcolor=white]; - edge [arrowhead=empty]; - rankdir=BT; - - MappedFileObject [label="MappedFileObject\n(from mapped_file_object)"]; - Dataspace [label="Dataspace\n(from dataspace)"]; - File [label="File\n(from file)"]; - MappedFile [label="MappedFile\n(from mapped_file)"]; - Sync [label="Sync\n(from sync)"]; - - MappedFileObject -> Dataspace; - MappedFileObject -> File; - MappedFileObject -> MappedFile; - MappedFileObject -> Sync; -} -}}} - -######## End of diagram. - -Defining a compound interface would be equivalent to defining an interface as -follows, if `idl` were to support this syntax: - -{{{ -interface MappedFileObject inherits Dataspace, File, MappedFile, Sync -{ -}; -}}} - -(Currently, this syntax is not supported due to limitations with the tool.) - -Just as with individual interface descriptions, rules are generated to ensure -that code supporting compound interfaces is generated from the interface -description files. - -Special variables act as manifests of all compound interface descriptions used -in a particular way: - -|| '''Variable''' || '''Interface Usage''' || -|| `COMP_INTERFACES_C` || C language server code || -|| `COMP_INTERFACES_CC` || C++ language server code || - -For example: - -{{{ -COMP_INTERFACES_CC = filesystem_object mapped_file_object -}}} - -Here, two compound interfaces are listed for generation as C++ components. -Thus, appropriate `_NAME` and `_INTERFACES` variables must be defined to -permit the necessary rule generation so that `make` can generate and build all -appropriate files. diff -r 7fa8fbaae4c4 -r 170967cc0f8b docs/wiki/Servers --- a/docs/wiki/Servers Fri Dec 09 19:33:15 2022 +0100 +++ b/docs/wiki/Servers Sat Dec 10 01:28:22 2022 +0100 @@ -1,14 +1,15 @@ = Servers = Server code is code that provides components in the form described by those -components' interfaces, employing interprocess communications mechanisms. +components' interfaces, employing interprocess communications mechanisms to +expose those components to [[Clients|clients]]. <> == Introduction == -The following example interface, resident in a file called `calc.idl`, will be -used to illustrate the mechanisms described in this document: +The following example interfaces will be used to illustrate the mechanisms +described in this document. Firstly, in `calc.idl`: {{{ interface Calc @@ -20,8 +21,26 @@ }; }}} -To expose this interface to other programs, a server program would do the -following: +Secondly, in `counter.idl`: + +{{{ +interface Counter +{ + void increment(out int result); +}; +}}} + +Finally, in `calc_counter.idl`: + +{{{ +import "calc.idl"; +import "counter.idl"; + +interface MappedFileObject composes Dataspace, File, Flush, MappedFile, Notification; +}}} + +To expose these interfaces to other programs, a server program would do the +following in each case: * Obtain appropriate program types to reference the interface @@ -54,19 +73,22 @@ == C Language Servers == -An object representing a server will have the `Calc` type. This encapsulates -a reference to the object state and a reference to the interface details. +A server exposing the `Calc` interface will employ an object having the `Calc` +type. This encapsulates a reference to the object state and a reference to the +interface details. Since the aim in implementing a server is to provide access to state information held within a process, the object state reference will refer to a -location holding such information via a pointer member. +location holding such information via a pointer member. The following could be +used for the state supporting the `Calc` interface. {{{ ref_Calc ref = {.ptr=0}; }}} Here, a value of `0` is used because the interface operations are stateless -(they act like plain functions): +(they act like plain functions), and so it does not really matter what `ptr` +is set to. The `Calc` object is initialised using the chosen reference value and a reference to interface information: @@ -89,8 +111,23 @@ }; }}} -The `Calc` object is then associated with a server capability and invoked when -incoming messages are directed towards it. +The `Calc` object is then [[#Exposing|associated with a server capability]] +and invoked when incoming messages are directed towards it. + +Where operations are not stateless and instead operate on some kind of state +or object, the reference is initialised as in this example involving the +`Counter` interface: + +{{{ +int counter = 0; +ref_Counter ref = {.ptr=&counter}; +}}} + +Here, the state modified by the `Counter` operations is a simple integer. As +long as the operations are written to interpret the state correctly, it does +not matter what kind of object provides the state. For C language servers, +there is not necessarily a requirement to have a dedicated data structure +containing the state. === Compound Interfaces === @@ -99,22 +136,26 @@ example involving a reference suitable for a `CalcCounter` object: {{{ -ref_CalcCounter ref = {.ptr=&counter}; +ref_CalcCounter ref = {.ptr=0, .as_Counter={.ptr=&counter}}; }}} -Here, the pointer member employs the address of a counter. This is accessed in -the appropriate operation function. +Here, the pointer member is given as `0` because there is no dedicated state +for the `CalcCounter` compound interface. However, when the component is to be +interpreted as a `Counter`, the `as_Counter` member provides the corresponding +reference, this indicating the address of a counter as the pointer to the +appropriate state, as previously suggested. -The object itself is populated in the same way as shown above: +The object employed by the server is itself populated in the same way as shown +above: {{{ CalcCounter obj = {.ref=ref, .iface=&server_iface_CalcCounter}; }}} -The principal difference involves the interface details. Since a compound -interface is exposed, there must be a way to address the individual -interfaces, and this is done using members employing a specific naming -convention: +The principal difference between simple and compound interface definition +involves the interface details. With a compound interface, there must be a way +to address the individual interfaces, and this is done using members employing +a specific naming convention: {{{ iface_CalcCounter server_iface_CalcCounter = { @@ -145,7 +186,9 @@ }}} Here, the pointer member is used to access the information referenced when the -object was initialised. +object was initialised. As noted above, the interpretation of the pointer +member is left to the operations, with the chosen interpretation being a +simple integer in this example. == C++ Language Servers == @@ -171,8 +214,8 @@ }; }}} -The `server_Calc` object is then associated with a server capability and -invoked when incoming messages are directed towards it. +The `server_Calc` object is then [[#Exposing|associated with a server +capability]] and invoked when incoming messages are directed towards it. === Compound Interfaces === @@ -215,6 +258,7 @@ } }}} +((Exposing)) == Exposing Objects as Servers == In L4Re, component objects can be made available to other programs by diff -r 7fa8fbaae4c4 -r 170967cc0f8b examples/directory_object.idl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/directory_object.idl Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,4 @@ +import "directory.idl"; +import "notification.idl"; + +interface DirectoryObject composes Directory, Notification; diff -r 7fa8fbaae4c4 -r 170967cc0f8b examples/filesystem_object.idl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/filesystem_object.idl Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,4 @@ +import "filesystem.idl"; +import "filesystem_factory.idl"; + +interface FilesystemObject composes Filesystem, FilesystemFactory; diff -r 7fa8fbaae4c4 -r 170967cc0f8b examples/mapped_file_object.idl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mapped_file_object.idl Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,7 @@ +import "dataspace.idl"; +import "file.idl"; +import "flush.idl"; +import "mapped_file.idl"; +import "notification.idl"; + +interface MappedFileObject composes Dataspace, File, Flush, MappedFile, Notification; diff -r 7fa8fbaae4c4 -r 170967cc0f8b examples/opener_context_object.idl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/opener_context_object.idl Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,4 @@ +import "dataspace.idl"; +import "opener_context.idl"; + +interface OpenerContextObject composes Dataspace, OpenerContext; diff -r 7fa8fbaae4c4 -r 170967cc0f8b examples/pager_object.idl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/pager_object.idl Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,4 @@ +import "region_mapper.idl"; +import "system_pager.idl"; + +interface PagerObject composes RegionMapper, SystemPager; diff -r 7fa8fbaae4c4 -r 170967cc0f8b examples/pipe_object.idl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/pipe_object.idl Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,6 @@ +import "dataspace.idl"; +import "flush.idl"; +import "notification.idl"; +import "pipe.idl"; + +interface PipeObject composes Dataspace, Flush, Notification, Pipe; diff -r 7fa8fbaae4c4 -r 170967cc0f8b idl.lex --- a/idl.lex Fri Dec 09 19:33:15 2022 +0100 +++ b/idl.lex Sat Dec 10 01:28:22 2022 +0100 @@ -42,9 +42,10 @@ {WS} /* discard */ ^"#include" return INCLUDE; -"<"[^>]*">" yylval.str = strdup(yytext); return HEADER; -\"[^"]*\" yylval.str = strdup(yytext); return HEADER; +"<"[^>]*">" yylval.str = strdup(yytext); return PHEADER; +\"[^"]*\" yylval.str = strdup(yytext); return QHEADER; +"import" return IMPORT; "interface" return INTERFACE; "in" yylval.num = IN_PARAMETER; return IN; @@ -66,6 +67,8 @@ "{" return BEGINSECTION; "}" return ENDSECTION; +"composes" return COMPOSES; + [_[:alpha:]][_[:alnum:]]* yylval.str = strdup(yytext); return IDENTIFIER; [[:digit:]]+ yylval.str = strdup(yytext); return DECIMAL; "0"[xX][[:xdigit:]]+ yylval.str = strdup(yytext); return HEXADECIMAL; diff -r 7fa8fbaae4c4 -r 170967cc0f8b idl.y --- a/idl.y Fri Dec 09 19:33:15 2022 +0100 +++ b/idl.y Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Grammar for a simple interface description language. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -33,7 +33,9 @@ /* State information, also reset by the function defined below. */ struct interface *last_interface = NULL; +struct import *last_import = NULL; struct include *last_include = NULL; +const char *output_dirname = NULL, *output_basename = NULL; %} %define parse.error verbose @@ -43,8 +45,10 @@ %union { long num; char *str; + struct import imp; struct include inc; struct interface iface; + struct interface_ref iface_ref; struct signature sig; struct identifier ident; struct parameter param; @@ -56,7 +60,7 @@ %type IDENTIFIER DECIMAL HEXADECIMAL %type interface_name %type qualifier operation -%type HEADER +%type header PHEADER QHEADER %type attr_param /* Specifiers and items have numeric values. */ @@ -68,9 +72,11 @@ /* Associate rules with the appropriate union members. */ +%type import %type include %type q_interface %type interface +%type interface_bases %type q_signature %type signature %type signatures @@ -83,7 +89,7 @@ /* Scanner token types. */ -%token INCLUDE HEADER +%token IMPORT INCLUDE PHEADER QHEADER %token INTERFACE %token BEGINSECTION ENDSECTION %token LPAR RPAR @@ -92,6 +98,7 @@ %token IN INOUT OUT %token CAP FPAGE %token IDENTIFIER DECIMAL HEXADECIMAL +%token COMPOSES /* Starting rule. */ @@ -99,24 +106,36 @@ %% +/* Parse each file, with last_interface yielding the parsing products. */ + file : statements - { write_files(last_interface); } ; statements : statement statements | %empty ; -statement : include +statement : import + | include | q_interface - { $1.tail = last_interface; last_interface = copy_interface($1); } + { $1.output_dirname = output_dirname; $1.output_basename = output_basename; + $1.tail = last_interface; last_interface = copy_interface($1); } ; -include : INCLUDE HEADER +import : IMPORT QHEADER TERM + { $$.filename = $2; $$.tail = last_import; last_import = copy_import($$); } + ; + +include : INCLUDE header { $$.filename = $2; $$.tail = last_include; last_include = copy_include($$); } ; -/* Interface qualified with or without attributes. */ +header : PHEADER + | QHEADER + ; + +/* Interface qualified with or without attributes, either having a signature + section or a list of base interfaces. */ q_interface : LBRACE attributes RBRACE interface { $4.attributes = copy_attribute($2); $$ = $4; } @@ -125,13 +144,26 @@ ; interface : INTERFACE interface_name BEGINSECTION signatures ENDSECTION TERM - { $$.name = $2; $$.signatures = copy_signature($4); $$.includes = last_include; last_include = NULL; } + { $$.name = $2; $$.bases = NULL; $$.signatures = copy_signature($4); + $$.imports = last_import; + $$.includes = last_include; last_include = NULL; } | INTERFACE interface_name BEGINSECTION ENDSECTION TERM - { $$.name = $2; $$.signatures = NULL; $$.includes = last_include; last_include = NULL; } + { $$.name = $2; $$.bases = NULL; $$.signatures = NULL; + $$.imports = last_import; + $$.includes = last_include; last_include = NULL; } + | INTERFACE interface_name COMPOSES interface_bases TERM + { $$.name = $2; $$.bases = copy_interface_ref($4); $$.signatures = NULL; + $$.imports = last_import; + $$.includes = last_include; last_include = NULL; } ; interface_name : IDENTIFIER ; +interface_bases : interface_name SEP interface_bases + { $$.name = $1; $$.iface = NULL; copy_tail(interface_ref, $$, $3); } + | interface_name + { $$.name = $1; $$.iface = NULL; $$.tail = NULL; } + /* Signatures qualified with or without attributes. */ signatures : q_signature signatures @@ -219,10 +251,13 @@ return v; } -void reset(void) +void reset(const char *dirname, const char *basename) { last_interface = NULL; + last_import = NULL; last_include = NULL; + output_dirname = dirname; + output_basename = basename; } void yyerror(const char *message) diff -r 7fa8fbaae4c4 -r 170967cc0f8b imports.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imports.c Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,288 @@ +/* + * File parsing and importing. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include "common.h" +#include "config.h" +#include "imports.h" +#include "program.h" + + + +/* Parser functions and settings. */ + +extern void yyparse(void); +extern void yyrestart(FILE *); +extern int yydebug; + +/* Specialised parser functions. */ + +extern void reset(const char *dirname, const char *basename); + +/* Parsing products. */ + +extern struct interface *last_interface; + +/* Input directory. */ + +static char *input_dirname = NULL; + + + +/* Obtain the basename and directory name of generated output. */ + +static int get_output_location(const char *filename, char **dirname, char **basename) +{ + char *ext; + + /* Obtain the basename of the output prefix. */ + + *basename = make_basename(filename); + + if (*basename == NULL) + return 0; + + /* Truncate the basename to remove the extension. */ + + ext = strrchr(*basename, '.'); + + if (ext != NULL) + *ext = '\0'; + + /* Obtain the directory name from the output prefix. */ + + if (conf.output_dir == NULL) + *dirname = make_dirname(filename); + else + *dirname = conf.output_dir; + + if (*dirname == NULL) + { + free(*basename); + return 0; + } + + return 1; +} + +/* Obtain a suitable filename from the parsed import details. */ + +static struct interface *import_interface_file(const char *quoted_filename) +{ + /* Allocate a string for the quoted filename minus quotes. */ + + size_t length = strlen(quoted_filename) - 1; + char filename[length]; + + /* For full paths, allocate enough space for both the dirname and the + filename. */ + + char pathname[strlen(input_dirname) + 1 + length]; + + /* Strip quotes from the filename token. */ + + strncpy(filename, quoted_filename + 1, length - 1); + filename[length - 1] = '\0'; + + /* Introduce the same dirname as the original input file. + NOTE: This should probably use a search path. */ + + if (is_basename(filename)) + { + sprintf(pathname, "%s/%s", input_dirname, filename); + return import_file(pathname, 0); + } + else + return import_file(filename, 0); +} + +/* Resolve imports in the interface description. */ + +static int resolve_imports(struct interface *iface) +{ + struct import *imp; + + for (; iface != NULL; iface = iface->tail) + { + /* Traverse the imports list to resolve imports. */ + + for (imp = iface->imports; imp != NULL; imp = imp->tail) + { + imp->iface = import_interface_file(imp->filename); + + if (imp->iface == NULL) + return 0; + } + } + + return 1; +} + +/* Populate an interface reference if the base is found. */ + +static int populate_base(struct interface_ref *base, struct interface *iface) +{ + if (!strcmp(iface->name, base->name)) + { + base->iface = iface; + return 1; + } + else + return 0; +} + +/* Find a base interface definition. */ + +static int find_base_in_imports(struct interface_ref *base, struct import *imp) +{ + struct interface *iface; + + for (; imp != NULL; imp = imp->tail) + { + for (iface = imp->iface; iface != NULL; iface = iface->tail) + { + if (populate_base(base, iface)) + return 1; + + /* Investigate imports related to an interface. */ + + if (find_base_in_imports(base, iface->imports)) + return 1; + } + } + + return 0; +} + +/* Find a base interface in the current file or in imports. */ + +static int find_base(struct interface_ref *base, struct interface *iface) +{ + struct interface *preceding; + + /* Search preceding interfaces in the same file. */ + + for (preceding = iface->tail; preceding != NULL; preceding = preceding->tail) + { + if (populate_base(base, preceding)) + return 1; + } + + /* Search imported files. */ + + if (find_base_in_imports(base, iface->imports)) + return 1; + + fprintf(stderr, "Could not locate base interface %s used by interface %s.\n", + base->name, iface->name); + return 0; +} + +/* Populate any base interfaces by traversing all interfaces and searching for + the base interface definitions. */ + +static int populate_bases(struct interface *iface) +{ + struct interface_ref *base; + + /* Traverse all interfaces. */ + + for (; iface != NULL; iface = iface->tail) + { + /* Check all bases. */ + + for (base = iface->bases; base != NULL; base = base->tail) + { + if (!find_base(base, iface)) + return 0; + } + } + + return 1; +} + + + +/* Return the last interface from the parsing of the given file, or NULL if + parsing or importing failed. */ + +struct interface *import_file(const char *filename, int top_level) +{ + struct interface *result; + FILE *fp; + char *dirname, *basename; + + /* Obtain output location details for interfaces within a file. */ + + if (!get_output_location(filename, &dirname, &basename)) + return NULL; + + /* Record the input dirname using the original input file. */ + + if (input_dirname == NULL) + { + input_dirname = make_dirname(filename); + if (input_dirname == NULL) + return NULL; + } + + fp = fopen(filename, "r"); + + if (fp == NULL) + { + fprintf(stderr, "Could not open file: %s\n", filename); + return NULL; + } + + /* Reset any specialised parser state and set the prefix to annotate parsing + products. */ + + reset(dirname, basename); + + yyrestart(fp); + yyparse(); + fclose(fp); + + /* Obtain the parsing products and process them. */ + + result = last_interface; + + if (!resolve_imports(result)) + return NULL; + + if (!populate_bases(result)) + return NULL; + + /* Either report basenames of processed files... */ + + if (conf.show_filenames) + printf("%s\n", basename); + + /* Or write files where directed. */ + + else + if (top_level || conf.generate_all) + write_files(result); + + return result; +} diff -r 7fa8fbaae4c4 -r 170967cc0f8b imports.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imports.h Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,26 @@ +/* + * File parsing and importing. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include "types.h" + +struct interface *import_file(const char *filename, int top_level); diff -r 7fa8fbaae4c4 -r 170967cc0f8b interface.c --- a/interface.c Fri Dec 09 19:33:15 2022 +0100 +++ b/interface.c Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Generation of interface headers. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,6 +20,7 @@ */ #include +#include #include "client.h" #include "common.h" #include "config.h" @@ -32,6 +33,78 @@ +/* Emit compound interface includes. */ + +static void write_compound_includes(FILE *fp, struct interface *iface) +{ + struct interface_ref *base; + + for (base = iface->bases; base != NULL; base = base->tail) + { + /* Only output includes where the base resides in a different file. */ + + if (strcmp(iface->output_basename, base->iface->output_basename)) + fprintf(fp, compound_interface_include, base->iface->output_basename); + } +} + +/* Write the reference type members. */ + +static void write_reference_type_members(FILE *fp, struct interface_ref *base) +{ + if (base == NULL) + return; + + /* Reverse the list by processing subsequent members first. */ + + write_reference_type_members(fp, base->tail); + + fprintf(fp, compound_ref_type_definition_member_c, + base->name, base->name); +} + +/* Generate a reference type for C language interfaces. */ + +static void write_reference_type(FILE *fp, struct interface *iface) +{ + struct interface *i; + + if (is_compound_interface(iface)) + { + fprintf(fp, compound_ref_type_definition_prologue_c, L4_CAP_TYPE); + + write_reference_type_members(fp, iface->bases); + + fprintf(fp, compound_ref_type_definition_epilogue_c, + iface->name); + } + else + fprintf(fp, ref_type_definition_c, L4_CAP_TYPE, iface->name); +} + +/* Augment compound interface class declarations. */ + +static void write_compound_interface_bases_c(FILE *fp, struct interface *iface) +{ + struct interface_ref *base; + + for (base = iface->bases; base != NULL; base = base->tail) + fprintf(fp, interface_body_base_c, base->name, base->name); +} + +/* Augment compound interface type declarations. */ + +static void write_compound_interface_bases_cpp(FILE *fp, struct interface *iface) +{ + struct interface_ref *base; + int first; + + for (base = iface->bases, first = 1; base != NULL; base = base->tail, first = 0) + fprintf(fp, interface_prologue_base_cpp, first ? " : " : ", ", base->name); +} + + + /* Write the definition of an interface to the given file for use by client or server components. Client headers employ such definitions as do server interface headers, whereas server wrapper headers employ other descriptions @@ -42,7 +115,7 @@ { int cpp = (conf.language == CPP_LANGUAGE); int client = (component == CLIENT_ROLE); - int input_items = get_max_input_items(iface->signatures); + int input_items = get_max_input_items(iface); char *class_name = get_interface_class_name(iface, component); char *name = iface->name; @@ -50,6 +123,7 @@ write_includes_separator(iface->includes, fp); write_includes(iface->includes, fp); + write_compound_includes(fp, iface); /* Generate interface abstractions. */ @@ -58,7 +132,7 @@ /* Define an object reference type for C. */ if (!client) - fprintf(fp, ref_type_definition_c, L4_CAP_TYPE, name); + write_reference_type(fp, iface); /* Define C client signatures representing the client functions. */ @@ -87,10 +161,22 @@ else fputs(interface_prologue_c, fp); + /* Augment the interface declaration with base interfaces for compound + interfaces. */ + + if (!client && cpp && is_compound_interface(iface)) + write_compound_interface_bases_cpp(fp, iface); + /* Start the class or type body. */ fputs(interface_body_begin, fp); + /* Augment the interface declaration with base interfaces for compound + interfaces. */ + + if (!client && !cpp && is_compound_interface(iface)) + write_compound_interface_bases_c(fp, iface); + /* Define any state and initialisation details of a class. */ if (cpp) @@ -98,7 +184,8 @@ if (client) fprintf(fp, client_interface_endpoint_declaration_cpp, L4_CAP_TYPE); - fputs(interface_signatures_prologue_cpp, fp); + if (iface->signatures != NULL) + fputs(interface_signatures_prologue_cpp, fp); /* Define a constructor. */ @@ -137,7 +224,7 @@ /* Emit opcodes and message access structures with the generic server interface, these being common to both client and server. */ - if (!client) + if (!client && (iface->signatures != NULL)) { write_opcode_definition(iface, fp); write_structures(iface->signatures, fp, iface); diff -r 7fa8fbaae4c4 -r 170967cc0f8b main.c --- a/main.c Fri Dec 09 19:33:15 2022 +0100 +++ b/main.c Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Main program. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,70 +26,11 @@ #include #include "common.h" #include "config.h" -#include "program.h" +#include "imports.h" +#include "summary.h" #include "templates.h" #include "version.h" -/* Parser functions and settings. */ - -extern void yyparse(void); -extern void yyrestart(FILE *); -extern int yydebug; - -/* Specialised parser functions. */ - -extern void reset(void); - - - -/* Return a copy of the given string without any extension. */ - -static char *make_prefix(const char *s) -{ - char *copy = strdup(s); - char *ext; - - if (copy == NULL) - return NULL; - - ext = strrchr(copy, '.'); - - if (ext != NULL) - *ext = '\0'; - - return copy; -} - -static int get_compound_prefix_conflict(int argc, char *argv[]) -{ - char *arg, *prefix, *name; - int index, result; - - for (index = optind; index < argc; index++) - { - arg = argv[index]; - prefix = make_prefix(arg); - result = 0; - - if (prefix != NULL) - { - name = make_basename(prefix); - - if (name != NULL) - { - result = strcasecmp(name, conf.compound); - free(name); - } - - free(prefix); - } - - if (!result) - return index; - } - - return 0; -} /* Option definitions. */ @@ -98,13 +39,13 @@ /* long opt following var pointer short opt */ {"all", no_argument, NULL, 'a' }, {"client", no_argument, NULL, 'c' }, - {"comp", required_argument, NULL, 'C' }, {"dir", required_argument, NULL, 'd' }, + {"files", no_argument, NULL, 'f' }, {"headers", no_argument, NULL, 'h' }, {"help", no_argument, NULL, '?' }, {"interfaces", no_argument, NULL, 'i' }, {"language", required_argument, NULL, 'l' }, - {"comp-name", required_argument, NULL, 'N' }, + {"recursive", no_argument, NULL, 'R' }, {"routines", no_argument, NULL, 'r' }, {"server", no_argument, NULL, 's' }, {"verbose", no_argument, NULL, 'v' }, @@ -117,19 +58,21 @@ int main(int argc, char *argv[]) { - FILE *fp; char *arg, *progname; int option, index, selected_content = 0, selected_role = 0; + struct interface *iface; #if YYDEBUG == 1 yydebug = 0; #endif + /* The configuration structure is already initialised in config.c. */ + /* Accept various options. */ while (1) { - option = getopt_long(argc, argv, "acC:d:hil:N:rsvV?", long_options, NULL); + option = getopt_long(argc, argv, "acd:fhil:rRsvV?", long_options, NULL); if (option == -1) break; @@ -149,19 +92,16 @@ selected_role = 1; break; - /* Select compound interface filename prefix and output. */ - case 'C': - conf.compound = optarg; - if (conf.compound_name == NULL) - conf.compound_name = conf.compound; - selected_role = 1; - break; - /* Set output directory. */ case 'd': conf.output_dir = optarg; break; + /* Only report processed file basenames. */ + case 'f': + conf.show_filenames = 1; + break; + /* Generate headers. */ case 'h': conf.headers = 1; @@ -187,20 +127,17 @@ } break; - /* Select compound interface name and output. */ - case 'N': - conf.compound_name = optarg; - if (conf.compound == NULL) - conf.compound = conf.compound_name; - selected_role = 1; - break; - /* Generate routines (definitions). */ case 'r': conf.routines = 1; selected_content = 1; break; + /* Also generate files associated with base interfaces. */ + case 'R': + conf.generate_all = 1; + break; + /* Select server output. */ case 's': conf.server = 1; @@ -241,18 +178,6 @@ conf.client = 1; conf.server = 1; } - /* Check any compound interface prefix against named files. */ - - if (conf.compound != NULL) - { - if ((index = get_compound_prefix_conflict(argc, argv))) - { - fprintf(stderr, "Compound interface prefix %s conflicts with filename %s.\n", - conf.compound, argv[index]); - return 1; - } - } - /* Produce an error without any input files. */ if (optind >= argc) @@ -261,52 +186,27 @@ return 1; } - /* Begin generating any compound interface code. */ - - if (!begin_compound_output()) - { - end_compound_output(); - return 1; - } - /* Process named files. */ for (index = optind; index < argc; index++) { arg = argv[index]; - /* Handle actual files. */ - - conf.output_prefix = make_prefix(arg); - - if (conf.output_prefix != NULL) - { - fp = fopen(arg, "r"); + /* Import each file for processing. */ - if (fp != NULL) - { - yyrestart(fp); - yyparse(); - fclose(fp); - - /* Reset any specialised parser state. */ + iface = import_file(arg, 1); - reset(); - } - - free(conf.output_prefix); + if (iface == NULL) + { + fprintf(stderr, "Could not process file: %s\n", arg); + return 1; + } - if (fp == NULL) - { - fprintf(stderr, "Could not open file: %s\n", arg); - return 1; - } - } + /* Summarise interfaces if requested. */ + + if (conf.verbose) + show_interfaces(iface); } - /* End generating any compound interface code. */ - - end_compound_output(); - return 0; } diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/client_interface_c.mk --- a/mk/client_interface_c.mk Fri Dec 09 19:33:15 2022 +0100 +++ b/mk/client_interface_c.mk Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # C client interface generation rules. # -# Copyright (C) 2020 Paul Boddie +# Copyright (C) 2020, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -18,8 +18,9 @@ # Boston, MA 02110-1301, USA define client_interface_c_template = +$(1)_CLIENT = $(call interfaces_to_client_c_h,$(1)) $(1)_CLIENT_IDL = $(call interfaces_to_idl,$(1)) -$(1)_CLIENT = $(call interfaces_to_client_c_h,$(1)) +$(1)_CLIENT_C_ = $(IDL_BUILD_DIR)/__client_c__$(1) # Reference interfaces if exported. @@ -27,6 +28,16 @@ $(1)_CLIENT_INC = $(call export_includes,$(call interfaces_to_interface_h,$(1))) endif -$$($(1)_CLIENT): $$($(1)_CLIENT_IDL) $$($(1)_CLIENT_INC) +# All generated files depend on a target file produced by a single invocation of +# the idl command. Where any generated files are missing, the invocation is +# performed again. + +$$(foreach FILENAME,$$($(1)_CLIENT),$$(if $$(wildcard $$(FILENAME)),,$$(shell rm -f $$($(1)_CLIENT_C_)))) + +$$($(1)_CLIENT): $$($(1)_CLIENT_C_) + @touch $$@ + +$$($(1)_CLIENT_C_): $$($(1)_CLIENT_IDL) $$($(1)_CLIENT_INC) $(IDL_PROG) $(IDL_PROG) -d $(IDL_BUILD_DIR) --client --headers --routines --language=c $$($(1)_CLIENT_IDL) + @touch $$@ endef diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/client_interface_cc.mk --- a/mk/client_interface_cc.mk Fri Dec 09 19:33:15 2022 +0100 +++ b/mk/client_interface_cc.mk Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # C++ client interface generation rules. # -# Copyright (C) 2020 Paul Boddie +# Copyright (C) 2020, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -18,8 +18,9 @@ # Boston, MA 02110-1301, USA define client_interface_cc_template = +$(1)_CLIENT = $(call interfaces_to_client_cc_h,$(1)) $(1)_CLIENT_IDL = $(call interfaces_to_idl,$(1)) -$(1)_CLIENT = $(call interfaces_to_client_cc_h,$(1)) +$(1)_CLIENT_CC_ = $(IDL_BUILD_DIR)/__client_cc__$(1) # Reference interfaces if exported. @@ -27,6 +28,16 @@ $(1)_CLIENT_INC = $(call export_includes,$(call interfaces_to_interface_h,$(1))) endif -$$($(1)_CLIENT): $$($(1)_CLIENT_IDL) $$($(1)_CLIENT_INC) +# All generated files depend on a target file produced by a single invocation of +# the idl command. Where any generated files are missing, the invocation is +# performed again. + +$$(foreach FILENAME,$$($(1)_CLIENT),$$(if $$(wildcard $$(FILENAME)),,$$(shell rm -f $$($(1)_CLIENT_CC_)))) + +$$($(1)_CLIENT): $$($(1)_CLIENT_CC_) + @touch $$@ + +$$($(1)_CLIENT_CC_): $$($(1)_CLIENT_IDL) $$($(1)_CLIENT_INC) $(IDL_PROG) $(IDL_PROG) -d $(IDL_BUILD_DIR) --client --headers --routines --language=c++ $$($(1)_CLIENT_IDL) + @touch $$@ endef diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/comp_interface_c.mk --- a/mk/comp_interface_c.mk Fri Dec 09 19:33:15 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -# C compound server interface generation rules. -# -# Copyright (C) 2020 Paul Boddie -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA - -define comp_interface_c_template = -$(1)_SERVER_IDL = $(call interfaces_to_idl,$(3)) -$(1)_SERVER = $(call interfaces_to_server_c_h,$(1)) -$(1)_SERVER_INC = $(call export_includes, $(call interfaces_to_interfaces_h,$(1))) -$(1)_SERVER_OPT = --comp=$(1) --comp-name=$(2) - -$$($(1)_SERVER): $$($(1)_SERVER_IDL) $$($(1)_SERVER_INC) - $(IDL_PROG) -d $(IDL_BUILD_DIR) $$($(1)_SERVER_OPT) --headers --routines --language=c $$($(1)_SERVER_IDL) -endef diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/comp_interface_cc.mk --- a/mk/comp_interface_cc.mk Fri Dec 09 19:33:15 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -# C++ compound server interface generation rules. -# -# Copyright (C) 2020 Paul Boddie -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA - -define comp_interface_cc_template = -$(1)_SERVER_IDL = $(call interfaces_to_idl,$(3)) -$(1)_SERVER = $(call interfaces_to_server_cc_h,$(1)) -$(1)_SERVER_INC = $(call export_includes, $(call interfaces_to_interfaces_h,$(1))) -$(1)_SERVER_OPT = --comp=$(1) --comp-name=$(2) - -$$($(1)_SERVER): $$($(1)_SERVER_IDL) $$($(1)_SERVER_INC) - $(IDL_PROG) -d $(IDL_BUILD_DIR) $$($(1)_SERVER_OPT) --headers --routines --language=c++ $$($(1)_SERVER_IDL) -endef diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/export_comp_interface_c.mk --- a/mk/export_comp_interface_c.mk Fri Dec 09 19:33:15 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -# C compound server interface exporting rules. -# -# Copyright (C) 2020 Paul Boddie -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA - -define export_comp_interface_c_template = -ifdef IDL_EXPORT_DIR -$(1)_INT_IDL = $(call interfaces_to_idl,$(3)) -$(1)_INT_INC = $(call export_includes, $(call interfaces_to_interfaces_h,$(1))) -$(1)_INT_OPT = --comp=$(1) --comp-name=$(2) - -$$($(1)_INT_INC): $$($(1)_INT_IDL) - -mkdir -p $(IDL_EXPORT_DIR) - $(IDL_PROG) -d $(IDL_EXPORT_DIR) $$($(1)_INT_OPT) --interfaces --language=c $$($(1)_INT_IDL) -endif -endef diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/export_comp_interface_cc.mk --- a/mk/export_comp_interface_cc.mk Fri Dec 09 19:33:15 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -# C++ compound server interface exporting rules. -# -# Copyright (C) 2020 Paul Boddie -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA - -define export_comp_interface_cc_template = -ifdef IDL_EXPORT_DIR -$(1)_INT_IDL = $(call interfaces_to_idl,$(3)) -$(1)_INT_INC = $(call export_includes, $(call interfaces_to_interfaces_h,$(1))) -$(1)_INT_OPT = --comp=$(1) --comp-name=$(2) - -$$($(1)_INT_INC): $$($(1)_INT_IDL) - -mkdir -p $(IDL_EXPORT_DIR) - $(IDL_PROG) -d $(IDL_EXPORT_DIR) $$($(1)_INT_OPT) --interfaces --language=c++ $$($(1)_INT_IDL) -endif -endef diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/export_interface_c.mk --- a/mk/export_interface_c.mk Fri Dec 09 19:33:15 2022 +0100 +++ b/mk/export_interface_c.mk Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # C interface exporting rules. # -# Copyright (C) 2020 Paul Boddie +# Copyright (C) 2020, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -22,7 +22,7 @@ $(1)_INT_IDL = $(call interfaces_to_idl,$(1)) $(1)_INT_INC = $(call export_includes,$(call interfaces_to_interface_h,$(1))) -$$($(1)_INT_INC): $$($(1)_INT_IDL) +$$($(1)_INT_INC): $$($(1)_INT_IDL) $(IDL_PROG) -mkdir -p $(IDL_EXPORT_DIR) $(IDL_PROG) -d $(IDL_EXPORT_DIR) --interfaces --language=c $$($(1)_INT_IDL) endif diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/export_interface_cc.mk --- a/mk/export_interface_cc.mk Fri Dec 09 19:33:15 2022 +0100 +++ b/mk/export_interface_cc.mk Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # C++ interface exporting rules. # -# Copyright (C) 2020 Paul Boddie +# Copyright (C) 2020, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -22,7 +22,7 @@ $(1)_INT_IDL = $(call interfaces_to_idl,$(1)) $(1)_INT_INC = $(call export_includes,$(call interfaces_to_interface_h,$(1))) -$$($(1)_INT_INC): $$($(1)_INT_IDL) +$$($(1)_INT_INC): $$($(1)_INT_IDL) $(IDL_PROG) -mkdir -p $(IDL_EXPORT_DIR) $(IDL_PROG) -d $(IDL_EXPORT_DIR) --interfaces --language=c++ $$($(1)_INT_IDL) endif diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/idl.mk --- a/mk/idl.mk Fri Dec 09 19:33:15 2022 +0100 +++ b/mk/idl.mk Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # Common definitions and functions. # -# Copyright (C) 2020 Paul Boddie +# Copyright (C) 2020, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -19,7 +19,7 @@ IDL_PROG = $(L4DIR)/idl4re/idl -# Functions to generate filenames. +# Functions to generate filenames for individual interfaces. export_includes = $(patsubst %,$(IDL_EXPORT_DIR)/%,$(1)) @@ -39,20 +39,6 @@ $(patsubst %,%_client.cc,$(1)) \ $(patsubst %,%_client.h,$(1)) \ -interfaces_to_server_c = \ - $(patsubst %,%_server.c,$(1)) \ - -interfaces_to_server_c_h = \ - $(patsubst %,%_server.c,$(1)) \ - $(patsubst %,%_server.h,$(1)) \ - -interfaces_to_server_cc = \ - $(patsubst %,%_server.cc,$(1)) \ - -interfaces_to_server_cc_h = \ - $(patsubst %,%_server.cc,$(1)) \ - $(patsubst %,%_server.h,$(1)) \ - interfaces_to_interface_h = \ $(patsubst %,%_interface.h,$(1)) \ @@ -60,11 +46,33 @@ $(patsubst %,%_interface.h,$(1)) \ $(patsubst %,%_interfaces.h,$(1)) \ -# For each interface , obtain a variable of the form _INTERFACES, -# accumulating words from each variable. The resulting list is sorted and -# without duplicate words. +_interfaces_to_server_c = \ + $(patsubst %,%_server.c,$(1)) \ + +_interfaces_to_server_c_h = \ + $(patsubst %,%_server.c,$(1)) \ + $(patsubst %,%_server.h,$(1)) \ + +_interfaces_to_server_cc = \ + $(patsubst %,%_server.cc,$(1)) \ + +_interfaces_to_server_cc_h = \ + $(patsubst %,%_server.cc,$(1)) \ + $(patsubst %,%_server.h,$(1)) \ -common_interfaces = $(sort $(foreach INTERFACE,$(1),$(value $(INTERFACE)_INTERFACES))) +# For each interface file basename, obtain basenames of all other files needed +# to complete the interfaces in the given file. The resulting list is sorted and +# without duplicate words. This function is used to support the indication of +# compound interfaces, providing the constituent interfaces. + +needed_interfaces = $(sort $(foreach BASENAME,$(call interfaces_to_idl,$(1)),$(shell $(IDL_PROG) -f $(BASENAME)))) + +# File generation for all needed interfaces. + +interfaces_to_server_c = $(call _interfaces_to_server_c,$(call needed_interfaces,$(1))) +interfaces_to_server_c_h = $(call _interfaces_to_server_c_h,$(call needed_interfaces,$(1))) +interfaces_to_server_cc = $(call _interfaces_to_server_cc,$(call needed_interfaces,$(1))) +interfaces_to_server_cc_h = $(call _interfaces_to_server_cc_h,$(call needed_interfaces,$(1))) # Generation of exported interface header file locations. diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/interface_rules.mk --- a/mk/interface_rules.mk Fri Dec 09 19:33:15 2022 +0100 +++ b/mk/interface_rules.mk Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # C and C++ rule generation for interfaces. # -# Copyright (C) 2020 Paul Boddie +# Copyright (C) 2020, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -22,8 +22,6 @@ include $(IDL_MK_DIR)/export_interface_c.mk include $(IDL_MK_DIR)/export_interface_cc.mk -include $(IDL_MK_DIR)/export_comp_interface_c.mk -include $(IDL_MK_DIR)/export_comp_interface_cc.mk # Generate routines. @@ -31,12 +29,10 @@ include $(IDL_MK_DIR)/client_interface_cc.mk include $(IDL_MK_DIR)/server_interface_c.mk include $(IDL_MK_DIR)/server_interface_cc.mk -include $(IDL_MK_DIR)/comp_interface_c.mk -include $(IDL_MK_DIR)/comp_interface_cc.mk # Extra rules to generate files from the interface descriptions. -# Generate an interface rule for each individual client interface. +# Generate an interface rule for each client interface. $(foreach INTERFACE,$(CLIENT_INTERFACES_C),\ $(eval $(call client_interface_c_template,$(INTERFACE)))) @@ -44,35 +40,18 @@ $(foreach INTERFACE,$(CLIENT_INTERFACES_CC),\ $(eval $(call client_interface_cc_template,$(INTERFACE)))) -# Generate an interface rule for each individual server interface. +# Generate an interface rule for each server interface. -$(foreach INTERFACE,$(SERVER_INTERFACES_C),\ +$(foreach INTERFACE,$(call needed_interfaces,$(SERVER_INTERFACES_C)),\ $(eval $(call server_interface_c_template,$(INTERFACE)))) -$(foreach INTERFACE,$(SERVER_INTERFACES_CC),\ +$(foreach INTERFACE,$(call needed_interfaces,$(SERVER_INTERFACES_CC)),\ $(eval $(call server_interface_cc_template,$(INTERFACE)))) -# Generate a compound interface rule for each compound interface, using details -# provided in the _NAME and _INTERFACES definitions. - -$(foreach INTERFACE,$(COMP_INTERFACES_C),\ -$(eval $(call comp_interface_c_template,$(INTERFACE),$(value $(INTERFACE)_NAME),$(value $(INTERFACE)_INTERFACES)))) +# Generate rules for exported interfaces. -$(foreach INTERFACE,$(COMP_INTERFACES_CC),\ -$(eval $(call comp_interface_cc_template,$(INTERFACE),$(value $(INTERFACE)_NAME),$(value $(INTERFACE)_INTERFACES)))) - -# Generate rules for exported individual interfaces. - -$(foreach INTERFACE,$(sort $(CLIENT_INTERFACES_C) $(SERVER_INTERFACES_C)),\ +$(foreach INTERFACE,$(call needed_interfaces,$(CLIENT_INTERFACES_C) $(SERVER_INTERFACES_C)),\ $(eval $(call export_interface_c_template,$(INTERFACE)))) -$(foreach INTERFACE,$(sort $(CLIENT_INTERFACES_CC) $(SERVER_INTERFACES_CC)),\ +$(foreach INTERFACE,$(call needed_interfaces,$(CLIENT_INTERFACES_CC) $(SERVER_INTERFACES_CC)),\ $(eval $(call export_interface_cc_template,$(INTERFACE)))) - -# Generate rules for exported compound interfaces. - -$(foreach INTERFACE,$(COMP_INTERFACES_C),\ -$(eval $(call export_comp_interface_c_template,$(INTERFACE),$(value $(INTERFACE)_NAME),$(value $(INTERFACE)_INTERFACES)))) - -$(foreach INTERFACE,$(COMP_INTERFACES_CC),\ -$(eval $(call export_comp_interface_cc_template,$(INTERFACE),$(value $(INTERFACE)_NAME),$(value $(INTERFACE)_INTERFACES)))) diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/server_interface_c.mk --- a/mk/server_interface_c.mk Fri Dec 09 19:33:15 2022 +0100 +++ b/mk/server_interface_c.mk Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # C server interface generation rules. # -# Copyright (C) 2020 Paul Boddie +# Copyright (C) 2020, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -18,8 +18,9 @@ # Boston, MA 02110-1301, USA define server_interface_c_template = -$(1)_SERVER = $(call interfaces_to_server_c_h,$(1)) +$(1)_SERVER = $(call _interfaces_to_server_c_h,$(1)) $(1)_SERVER_IDL = $(call interfaces_to_idl,$(1)) +$(1)_SERVER_C_ = $(IDL_BUILD_DIR)/__server_c__$(1) # Reference interfaces if exported. @@ -27,6 +28,16 @@ $(1)_SERVER_INC = $(call export_includes,$(call interfaces_to_interface_h,$(1))) endif -$$($(1)_SERVER): $$($(1)_SERVER_IDL) $$($(1)_SERVER_INC) +# All generated files depend on a target file produced by a single invocation of +# the idl command. Where any generated files are missing, the invocation is +# performed again. + +$$(foreach FILENAME,$$($(1)_SERVER),$$(if $$(wildcard $$(FILENAME)),,$$(shell rm -f $$($(1)_SERVER_C_)))) + +$$($(1)_SERVER): $$($(1)_SERVER_C_) + @touch $$@ + +$$($(1)_SERVER_C_): $$($(1)_SERVER_IDL) $$($(1)_SERVER_INC) $(IDL_PROG) $(IDL_PROG) -d $(IDL_BUILD_DIR) --server --headers --routines --language=c $$($(1)_SERVER_IDL) + @touch $$@ endef diff -r 7fa8fbaae4c4 -r 170967cc0f8b mk/server_interface_cc.mk --- a/mk/server_interface_cc.mk Fri Dec 09 19:33:15 2022 +0100 +++ b/mk/server_interface_cc.mk Sat Dec 10 01:28:22 2022 +0100 @@ -1,6 +1,6 @@ # C++ server interface generation rules. # -# Copyright (C) 2020 Paul Boddie +# Copyright (C) 2020, 2022 Paul Boddie # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -18,8 +18,9 @@ # Boston, MA 02110-1301, USA define server_interface_cc_template = -$(1)_SERVER = $(call interfaces_to_server_cc_h,$(1)) +$(1)_SERVER = $(call _interfaces_to_server_cc_h,$(1)) $(1)_SERVER_IDL = $(call interfaces_to_idl,$(1)) +$(1)_SERVER_CC_ = $(IDL_BUILD_DIR)/__server_cc__$(1) # Reference interfaces if exported. @@ -27,6 +28,16 @@ $(1)_SERVER_INC = $(call export_includes,$(call interfaces_to_interface_h,$(1))) endif -$$($(1)_SERVER): $$($(1)_SERVER_IDL) $$($(1)_SERVER_INC) +# All generated files depend on a target file produced by a single invocation of +# the idl command. Where any generated files are missing, the invocation is +# performed again. + +$$(foreach FILENAME,$$($(1)_SERVER),$$(if $$(wildcard $$(FILENAME)),,$$(shell rm -f $$($(1)_SERVER_CC_)))) + +$$($(1)_SERVER): $$($(1)_SERVER_CC_) + @touch $$@ + +$$($(1)_SERVER_CC_): $$($(1)_SERVER_IDL) $$($(1)_SERVER_INC) $(IDL_PROG) $(IDL_PROG) -d $(IDL_BUILD_DIR) --server --headers --routines --language=c++ $$($(1)_SERVER_IDL) + @touch $$@ endef diff -r 7fa8fbaae4c4 -r 170967cc0f8b parser.h --- a/parser.h Fri Dec 09 19:33:15 2022 +0100 +++ b/parser.h Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Parser definitions. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -31,8 +31,10 @@ #define copy_attribute(in) (struct attribute *) copy(&in); #define copy_identifier(in) (struct identifier *) copy(&in); +#define copy_import(in) (struct import *) copy(&in); #define copy_include(in) (struct include *) copy(&in); #define copy_interface(in) (struct interface *) copy(&in); +#define copy_interface_ref(in) (struct interface_ref *) copy(&in); #define copy_parameter(in) (struct parameter *) copy(&in); #define copy_signature(in) (struct signature *) copy(&in); diff -r 7fa8fbaae4c4 -r 170967cc0f8b pkg/idl4re-examples/calc++/Makefile --- a/pkg/idl4re-examples/calc++/Makefile Fri Dec 09 19:33:15 2022 +0100 +++ b/pkg/idl4re-examples/calc++/Makefile Sat Dec 10 01:28:22 2022 +0100 @@ -13,15 +13,10 @@ include $(IDL_MK_DIR)/idl.mk -calc_counter_NAME = CalcCounter -calc_counter_INTERFACES = calc counter - -COMP_INTERFACES_CC = calc_counter - -# Individual interfaces. +# Required interfaces. CLIENT_INTERFACES_CC = calc counter -SERVER_INTERFACES_CC = calc counter +SERVER_INTERFACES_CC = calc counter calc_counter # Generated and plain source files. @@ -29,9 +24,7 @@ SERVER_INTERFACES_SRC_CC_calc_server++ = $(call interfaces_to_server_cc,calc) CLIENT_INTERFACES_SRC_CC_calc_counter_client++ = $(call interfaces_to_client_cc,calc counter) -SERVER_INTERFACES_SRC_CC_calc_counter_server++ = \ - $(call interfaces_to_server_cc,calc counter) \ - $(call interfaces_to_server_cc,calc_counter) +SERVER_INTERFACES_SRC_CC_calc_counter_server++ = $(call interfaces_to_server_cc,$(SERVER_INTERFACES_CC)) PLAIN_SRC_CC_calc_client++ = client.cc calc_local.cc PLAIN_SRC_CC_calc_server++ = server.cc diff -r 7fa8fbaae4c4 -r 170967cc0f8b pkg/idl4re-examples/calc/Makefile --- a/pkg/idl4re-examples/calc/Makefile Fri Dec 09 19:33:15 2022 +0100 +++ b/pkg/idl4re-examples/calc/Makefile Sat Dec 10 01:28:22 2022 +0100 @@ -13,15 +13,10 @@ include $(IDL_MK_DIR)/idl.mk -calc_counter_NAME = CalcCounter -calc_counter_INTERFACES = calc counter - -COMP_INTERFACES_C = calc_counter - -# Individual interfaces. +# Required interfaces. CLIENT_INTERFACES_C = calc counter -SERVER_INTERFACES_C = calc counter +SERVER_INTERFACES_C = calc counter calc_counter # Generated and plain source files. @@ -29,9 +24,7 @@ CLIENT_INTERFACES_SRC_C_calc_counter_client = $(call interfaces_to_client_c,calc counter) SERVER_INTERFACES_SRC_C_calc_server = $(call interfaces_to_server_c,calc) -SERVER_INTERFACES_SRC_C_calc_counter_server = \ - $(call interfaces_to_server_c,calc counter) \ - $(call interfaces_to_server_c,calc_counter) +SERVER_INTERFACES_SRC_C_calc_counter_server = $(call interfaces_to_server_c,$(SERVER_INTERFACES_C)) PLAIN_SRC_C_calc_client = client.c calc_local.c PLAIN_SRC_C_calc_server = server.c diff -r 7fa8fbaae4c4 -r 170967cc0f8b pkg/idl4re-examples/calc/server_compound.c --- a/pkg/idl4re-examples/calc/server_compound.c Fri Dec 09 19:33:15 2022 +0100 +++ b/pkg/idl4re-examples/calc/server_compound.c Sat Dec 10 01:28:22 2022 +0100 @@ -112,7 +112,7 @@ { /* Reference to state information and an encapsulation. */ - ref_CalcCounter ref = {.ptr=&counter}; + ref_CalcCounter ref = {.ptr=0, .as_Counter={.ptr=&counter}}; CalcCounter obj = {.ref=ref, .iface=&server_iface_CalcCounter}; /* Wait for messages, dispatching to the handler. */ diff -r 7fa8fbaae4c4 -r 170967cc0f8b pkg/idl4re-examples/idl/calc_counter.idl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/idl4re-examples/idl/calc_counter.idl Sat Dec 10 01:28:22 2022 +0100 @@ -0,0 +1,4 @@ +import "calc.idl"; +import "counter.idl"; + +interface CalcCounter composes Calc, Counter; diff -r 7fa8fbaae4c4 -r 170967cc0f8b program.c --- a/program.c Fri Dec 09 19:33:15 2022 +0100 +++ b/program.c Sat Dec 10 01:28:22 2022 +0100 @@ -30,330 +30,39 @@ #include "message.h" #include "program.h" #include "server.h" -#include "summary.h" #include "templates.h" -/* Current filename details. */ - -char *output_basename = NULL, *output_dirname = NULL; - /* Client, server and common files. */ FILE *client_fp = NULL, *client_header_fp = NULL, *server_fp = NULL, *server_header_fp = NULL, *interface_fp = NULL; -/* Compound interface and dispatcher files. */ - -FILE *compound_dispatch_fp = NULL, *compound_dispatch_header_fp = NULL, - *compound_interface_fp = NULL, *compound_interfaces_fp = NULL, - *compound_interface_type_fp = NULL; - -/* Processed interfaces. */ - -int processed_interfaces; - -/* Maximum number of input items expected by an interface. */ - -int max_input_items = 0; - - - -/* Return the first interface name as the output prefix. */ - -static char *get_output_prefix(struct interface *i) -{ - if (conf.output_prefix != NULL) - return conf.output_prefix; - - if (i == NULL) - return NULL; - - for (; i->tail != NULL; i = i->tail); - return i->name; -} - -/* Obtain the basename and directory name of generated output. */ - -static int set_output_location(struct interface *iface) -{ - /* Obtain a prefix for the output files. */ - - conf.output_prefix = get_output_prefix(iface); - - if (conf.output_prefix == NULL) - return 0; - - /* Obtain the basename of the output prefix. */ - - output_basename = make_basename(conf.output_prefix); - - if (output_basename == NULL) - return 0; - - /* Obtain the directory name from the output prefix. */ - - if (conf.output_dir == NULL) - output_dirname = make_dirname(conf.output_prefix); - else - output_dirname = conf.output_dir; - - if (output_dirname == NULL) - { - free(output_basename); - return 0; - } - - return 1; -} - -/* Generate compound code output, if requested. */ - -int begin_compound_output(void) -{ - char *compound_dirname = conf.output_dir != NULL ? conf.output_dir : "."; - char *s; - - if (conf.compound == NULL) - return 1; - - processed_interfaces = 0; - - if (conf.headers) - { - compound_dispatch_header_fp = get_output_file(server_header_filename, - compound_dirname, conf.compound); - if (compound_dispatch_header_fp == NULL) - return 0; - } - - if (conf.interfaces) - { - compound_interface_fp = get_output_file(interface_filename, - compound_dirname, conf.compound); - if (compound_interface_fp == NULL) - return 0; - - compound_interface_type_fp = get_output_file(compound_interface_type_filename, - compound_dirname, conf.compound); - if (compound_interface_type_fp == NULL) - return 0; - - compound_interfaces_fp = get_output_file(compound_interfaces_filename, - compound_dirname, conf.compound); - if (compound_interfaces_fp == NULL) - return 0; - } - - if (conf.routines) - { - s = (conf.language == CPP_LANGUAGE) ? server_filename_cpp - : server_filename_c; - compound_dispatch_fp = get_output_file(s, compound_dirname, conf.compound); - if (compound_dispatch_fp == NULL) - return 0; - } - - /* Emit prologues. */ +/* Generate functions corresponding to the signatures. */ - if (compound_dispatch_fp != NULL) - { - fprintf(compound_dispatch_fp, compound_dispatch_prologue, conf.compound); - - /* Write the handle function and dispatch function prologue. */ - - write_handler_signature(conf.compound_name, DEFINITION_ROLE, - compound_dispatch_fp); - fprintf(compound_dispatch_fp, handle_function, conf.compound_name); - fputs(END_FUNCTION, compound_dispatch_fp); - - write_dispatcher_signature(conf.compound_name, DEFINITION_ROLE, - compound_dispatch_fp); - fputs(compound_dispatch_function_prologue, compound_dispatch_fp); - } - - if (compound_dispatch_header_fp != NULL) - { - fprintf(compound_dispatch_header_fp, compound_dispatch_header_prologue, - conf.compound); - - /* Write the handler and dispatch signatures. */ +static void write_functions(struct signature *sig, FILE *fp, + struct interface *iface, + void (*write_function)(struct signature *, FILE *, struct interface *)) +{ + if (sig == NULL) + return; - write_handler_signature(conf.compound_name, DECLARATION_ROLE, - compound_dispatch_header_fp); - - write_dispatcher_signature(conf.compound_name, DECLARATION_ROLE, - compound_dispatch_header_fp); - fputs("\n", compound_dispatch_header_fp); - } - - if (compound_interface_fp != NULL) - fprintf(compound_interface_fp, - (conf.language == CPP_LANGUAGE) ? compound_interface_prologue_cpp - : compound_interface_prologue_c, - conf.compound, conf.compound_name); - - if (compound_interface_type_fp != NULL) - { - /* Begin reference type. */ - - if (conf.language != CPP_LANGUAGE) - fprintf(compound_interface_type_fp, compound_ref_type_definition_prologue_c, - conf.compound, L4_CAP_TYPE); - } - - if (compound_interfaces_fp != NULL) - fputs(compound_interfaces_prologue, compound_interfaces_fp); - - return 1; + write_function(sig, fp, iface); + write_functions(sig->tail, fp, iface, write_function); } -void write_compound_output(struct interface *iface) -{ - char *protocol, *ref; - int input_items; - - if (compound_dispatch_fp != NULL) - { - protocol = get_protocol(iface->attributes); - - /* Populate a function dispatching to each interface-level function where - the same protocol applies to an entire interface. */ - - if (protocol != NULL) - { - ref = get_object_conversion(iface, 1); - - fprintf(compound_dispatch_fp, dispatch_function_interface_case, - protocol, iface->name, ref); - - free(ref); - } - - /* Or dispatch to each operation defined at this level. */ - - else - write_dispatcher_cases(iface->signatures, compound_dispatch_fp, iface, 1); - } - - if (compound_dispatch_header_fp != NULL) - { - /* Compute the maximum number of items expected by each interface. */ - - input_items = get_max_input_items(iface->signatures); - - if (input_items > max_input_items) - max_input_items = input_items; - } - - /* Add this interface to the compound interface. */ - - if (compound_interface_fp != NULL) - write_compound_interface(iface); - - /* Add this interface's header to the compound interface includes. */ - - if (compound_interfaces_fp != NULL) - write_include(output_basename, "_interface.h", compound_interfaces_fp); - - processed_interfaces++; -} - -/* Generate includes for the server and common declarations from each input - file. */ - -void write_compound_dispatch_include(void) -{ - if (compound_dispatch_header_fp != NULL) - write_include(output_basename, "_server.h", compound_dispatch_header_fp); -} - -/* Augment a compound interface class declaration. */ - -void write_compound_interface(struct interface *iface) -{ - char *sep; +/* Write includes for base interfaces. */ - if (conf.language == CPP_LANGUAGE) - { - sep = processed_interfaces ? ", " : " "; - fprintf(compound_interface_fp, "%spublic %s", sep, iface->name); - } - else - fprintf(compound_interface_fp, " iface_%s *to_%s;\n", iface->name, iface->name); - - /* Include reference type. */ - - if (conf.language != CPP_LANGUAGE) - fprintf(compound_interface_type_fp, compound_ref_type_definition_member_c, - iface->name, iface->name); -} - -void end_compound_output(void) +static void write_server_includes(struct interface *iface, FILE *fp) { - /* Close the files. */ - - if (compound_dispatch_fp != NULL) - { - fputs(compound_dispatch_epilogue, compound_dispatch_fp); - fputs(END_FUNCTION, compound_dispatch_fp); - - /* Emit default configuration instance definition. */ - - fprintf(compound_dispatch_fp, server_config_instance, - conf.compound_name, conf.compound_name, conf.compound_name); - - fclose(compound_dispatch_fp); - } - - if (compound_dispatch_header_fp != NULL) - { - fprintf(compound_dispatch_header_fp, expected_items_definition, - conf.compound_name, max_input_items); + struct interface_ref *base; - /* Emit default configuration instance declaration. */ - - fprintf(compound_dispatch_header_fp, server_config_instance_declaration, - conf.compound_name); - - fclose(compound_dispatch_header_fp); - } - - if (compound_interface_fp != NULL) - { - if (conf.language == CPP_LANGUAGE) - fputs(compound_interface_epilogue_cpp, compound_interface_fp); - else - { - fprintf(compound_interface_fp, compound_interface_epilogue_c, - conf.compound_name); - - /* Include object type. */ - - fprintf(compound_interface_fp, object_type_definition_c, - conf.compound_name, conf.compound_name, conf.compound_name); - } - - fclose(compound_interface_fp); - } - - if (compound_interface_type_fp != NULL) - { - /* Complete reference type. */ - - if (conf.language != CPP_LANGUAGE) - fprintf(compound_interface_type_fp, compound_ref_type_definition_epilogue_c, - conf.compound_name); - - fclose(compound_interface_type_fp); - } - - if (compound_interfaces_fp != NULL) - fclose(compound_interfaces_fp); + for (base = iface->bases; base != NULL; base = base->tail) + write_include(base->iface->output_basename, "_server.h", fp); } @@ -364,29 +73,22 @@ { char *filename; - /* NOTE: Should exit with an error. */ - - if (!set_output_location(iface)) - return; - - /* Include server details for a compound interface dispatch function. */ - - write_compound_dispatch_include(); - - /* Open the separate files and write the details of each interface. */ - if (conf.headers) { if (conf.client) { - client_header_fp = get_output_file(client_header_filename, output_dirname, output_basename); + client_header_fp = get_output_file(client_header_filename, + iface->output_dirname, + iface->output_basename); if (client_header_fp == NULL) goto finalisation; } if (conf.server) { - server_header_fp = get_output_file(server_header_filename, output_dirname, output_basename); + server_header_fp = get_output_file(server_header_filename, + iface->output_dirname, + iface->output_basename); if (server_header_fp == NULL) goto finalisation; } @@ -394,7 +96,8 @@ if (conf.interfaces) { - interface_fp = get_output_file(interface_filename, output_dirname, output_basename); + interface_fp = get_output_file(interface_filename, iface->output_dirname, + iface->output_basename); if (interface_fp == NULL) goto finalisation; } @@ -403,16 +106,20 @@ { if (conf.client) { - filename = (conf.language == CPP_LANGUAGE) ? client_filename_cpp : client_filename_c; - client_fp = get_output_file(filename, output_dirname, output_basename); + filename = (conf.language == CPP_LANGUAGE) ? client_filename_cpp + : client_filename_c; + client_fp = get_output_file(filename, iface->output_dirname, + iface->output_basename); if (client_fp == NULL) goto finalisation; } if (conf.server) { - filename = (conf.language == CPP_LANGUAGE) ? server_filename_cpp : server_filename_c; - server_fp = get_output_file(filename, output_dirname, output_basename); + filename = (conf.language == CPP_LANGUAGE) ? server_filename_cpp + : server_filename_c; + server_fp = get_output_file(filename, iface->output_dirname, + iface->output_basename); if (server_fp == NULL) goto finalisation; } @@ -421,13 +128,15 @@ /* Emit prologues. */ if (client_fp != NULL) - fprintf(client_fp, client_prologue, output_basename, output_basename); + fprintf(client_fp, client_prologue, iface->output_basename, + iface->output_basename); if (client_header_fp != NULL) fputs(header_prologue, client_header_fp); if (server_fp != NULL) - fprintf(server_fp, server_prologue, output_basename, output_basename); + fprintf(server_fp, server_prologue, iface->output_basename, + iface->output_basename); if (server_header_fp != NULL) fputs(server_header_prologue, server_header_fp); @@ -457,50 +166,44 @@ if (interface_fp != NULL) fclose(interface_fp); - - /* Free the names. */ - - if (conf.output_dir == NULL) - free(output_dirname); - - free(output_basename); } -/* Generate the source file corresponding to the interface. */ +/* Generate source file content corresponding to each interface in turn. */ void write_interfaces(struct interface *iface) { - int input_items; - if (iface == NULL) return; - /* The list is reversed. */ + /* The list is reversed by recursing to subsequent interfaces and thus + processing them first. */ write_interfaces(iface->tail); - /* Write this particular interface after all referenced ones. */ - - if (conf.verbose) - show_interface(iface); - - /* Record details of this interface in the compound output. */ - - write_compound_output(iface); - /* Emit function definitions. */ if (client_fp != NULL) { - write_functions(iface->signatures, client_fp, iface, - write_client_function); + /* Do not write client definitions for compound interfaces. */ - if (conf.language == C_LANGUAGE) - write_client_interface(iface, client_fp); + if (!is_compound_interface(iface)) + { + write_functions(iface->signatures, client_fp, iface, + write_client_function); + + if (conf.language == C_LANGUAGE) + write_client_interface(iface, client_fp); + } } if (server_fp != NULL) { + /* Write any includes for base interfaces. */ + + write_server_includes(iface, server_fp); + + /* Write the dispatcher and handler functions. */ + write_functions(iface->signatures, server_fp, iface, write_server_function); @@ -510,7 +213,7 @@ fprintf(server_fp, handle_function, iface->name); fputs(END_FUNCTION, server_fp); - write_dispatcher(iface->signatures, server_fp, iface); + write_dispatcher(server_fp, iface); /* Emit default configuration instance definition. */ @@ -523,18 +226,24 @@ if (client_header_fp != NULL) { /* Emit interface include to obtain the type employed by the client - interface. */ + interface, doing this once per file. */ + + if (iface->tail == NULL) + write_include(iface->output_basename, "_interface.h", client_header_fp); - write_include(output_basename, "_interface.h", client_header_fp); + /* Do not write client definitions for compound interfaces. */ - write_interface_definition(iface, CLIENT_ROLE, client_header_fp); + if (!is_compound_interface(iface)) + write_interface_definition(iface, CLIENT_ROLE, client_header_fp); } if (server_header_fp != NULL) { - /* Emit interface include to obtain the interface type. */ + /* Emit interface include to obtain the interface type, doing this once per + file. */ - write_include(output_basename, "_interface.h", server_header_fp); + if (iface->tail == NULL) + write_include(iface->output_basename, "_interface.h", server_header_fp); /* Emit signatures. */ @@ -556,15 +265,3 @@ if (interface_fp != NULL) write_interface_definition(iface, SERVER_ROLE, interface_fp); } - -/* Generate functions corresponding to the signatures. */ - -void write_functions(struct signature *sig, FILE *fp, struct interface *iface, - void (*write_function)(struct signature *, FILE *, struct interface *)) -{ - if (sig == NULL) - return; - - write_function(sig, fp, iface); - write_functions(sig->tail, fp, iface, write_function); -} diff -r 7fa8fbaae4c4 -r 170967cc0f8b program.h --- a/program.h Fri Dec 09 19:33:15 2022 +0100 +++ b/program.h Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* - * Client code generation. + * Code generation from interface descriptions. * - * Copyright (C) 2019, 2020 Paul Boddie + * Copyright (C) 2019, 2020, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -24,20 +24,7 @@ #include #include "types.h" -/* Component-level processing. */ - -int begin_compound_output(void); -void write_compound_output(struct interface *iface); -void write_compound_dispatch_include(void); -void write_compound_interface(struct interface *iface); -void end_compound_output(void); - /* Interface processing. */ void write_files(struct interface *iface); void write_interfaces(struct interface *iface); - -/* Function details. */ - -void write_functions(struct signature *sig, FILE *fp, struct interface *iface, - void (*write_function)(struct signature *, FILE *, struct interface *)); diff -r 7fa8fbaae4c4 -r 170967cc0f8b server.c --- a/server.c Fri Dec 09 19:33:15 2022 +0100 +++ b/server.c Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Server code generation. * - * Copyright (C) 2019, 2020 Paul Boddie + * Copyright (C) 2019, 2020, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -29,32 +29,6 @@ -/* Generate each component operation signature or "function prototype". */ - -void write_server_interface_signature(struct signature *sig, - enum signature_role role, FILE *fp, - struct interface *iface) -{ - write_interface_signature(sig, role, SERVER_ROLE, - get_server_function_role(sig), fp, iface); -} - -/* Generate signature declarations for each operation. */ - -void write_server_signature(struct signature *sig, enum signature_role role, - FILE *fp, struct interface *iface) -{ - if (have_attribute(sig->attributes, "completion")) - write_server_initiation_signature(sig, role, fp, iface); - else - write_server_wrapper_signature(sig, role, fp, iface); - - /* Write the appropriate completion signature even for use by conventional - operations. */ - - write_server_completion_signature(sig, role, fp, iface); -} - /* Generic signature generation. */ static void _write_server_signature(struct signature *sig, @@ -70,30 +44,31 @@ free(opname); } -/* Generate operation wrapper function details used by the server. */ +/* Generate a signature for the invocation of an operation. */ -void write_server_wrapper_signature(struct signature *sig, enum signature_role role, +static void write_server_wrapper_signature(struct signature *sig, enum signature_role role, FILE *fp, struct interface *iface) { _write_server_signature(sig, role, fp, iface, "wrap"); } -void write_server_initiation_signature(struct signature *sig, +/* Generate a signature for the initiation of an operation. */ + +static void write_server_initiation_signature(struct signature *sig, enum signature_role role, FILE *fp, struct interface *iface) { _write_server_signature(sig, role, fp, iface, "initiate"); } -void write_server_completion_signature(struct signature *sig, +/* Generate a signature featuring an initiator reference (for genuine decoupled + completions) and only "out" and "inout" parameters. */ + +static void write_server_completion_signature(struct signature *sig, enum signature_role role, FILE *fp, struct interface *iface) { char *opname = get_operation_name(iface, sig); - - /* Generate a signature featuring an initiator reference (for genuine - decoupled completions) and only "out" and "inout" parameters. */ - int completion = have_attribute(sig->attributes, "completion"); char *prologue = completion ? @@ -113,36 +88,104 @@ free(opname); } -/* Generate function source code for each operation, with the generated function - unpacking a message, calling the actual operation and repacking the - results. */ +/* Generate a cast for superfluous message structures. */ + +static void write_superfluous_message_cast(FILE *fp, int input_words, int input_items, + int output_words, int output_items) +{ + if (!input_words && !input_items && !output_words && !output_items) + fputs(server_function_body_unused_message, fp); +} + +/* Generate the initialisation of input words and items. */ -void write_server_function(struct signature *sig, FILE *fp, struct interface *iface) +static void write_input_initialisation(struct parameter *param, FILE *fp, + const char *opname, int input_words, + int input_items) +{ + if (input_words) + { + write_accessor_initialisation(IN_PARAMETER, SERVER_ROLE, GENERAL_FUNCTION_ROLE, + opname, fp); + write_message_access(param, SERVER_ROLE, GENERAL_FUNCTION_ROLE, IN_PARAMETER, + WORD_CLASS, fp); + } + + if (input_items) + { + fputs("\n", fp); + write_message_access(param, SERVER_ROLE, GENERAL_FUNCTION_ROLE, IN_PARAMETER, + ITEM_CLASS, fp); + } +} + +/* Generate the initialisation of output words and items. */ + +static void write_output_initialisation(struct parameter *param, FILE *fp, + const char *opname, int output_words, + int output_items, enum function_role function) { - if (have_attribute(sig->attributes, "completion")) + if (output_words) + { + write_accessor_initialisation(OUT_PARAMETER, SERVER_ROLE, function, + opname, fp); + write_message_access(param, SERVER_ROLE, function, OUT_PARAMETER, + WORD_CLASS, fp); + } + + if (output_items) { - write_server_initiation_signature(sig, DEFINITION_ROLE, fp, iface); - write_server_initiation_function_body(sig->parameters, fp, iface, sig); - fputs(END_FUNCTION, fp); + fputs("\n", fp); + write_message_access(param, SERVER_ROLE, function, OUT_PARAMETER, + ITEM_CLASS, fp); } +} + +/* Generate an invocation of the actual server operation. */ + +static void write_server_function_call(struct parameter *param, FILE *fp, + struct interface *iface, + struct signature *sig, + enum specifier specifier) +{ + int continuing = 0; + char *name, *addr, + *opname = get_signature_operation_name(iface, sig, SERVER_ROLE, + ACCESS_ROLE, conf.language); + + fputs("\n err = ", fp); + + /* Access a method when emitting C++. */ + + if (conf.language == CPP_LANGUAGE) + fprintf(fp, "_self->%s(", opname); + + /* Employ a function pointer via the object type otherwise. */ + else { - write_server_wrapper_signature(sig, DEFINITION_ROLE, fp, iface); - write_server_wrapper_function_body(sig->parameters, fp, iface, sig); - fputs(END_FUNCTION, fp); + fprintf(fp, "_self->iface->%s(_self->ref", opname); + continuing = 1; } - /* Write the appropriate completion function even for use by conventional - operations. */ + /* Generate the parameter list, employing addresses for output parameters. */ + + write_parameters(param, fp, INVOCATION_ROLE, GENERAL_FUNCTION_ROLE, specifier, + continuing); + fputs(");\n", fp); - write_server_completion_signature(sig, DEFINITION_ROLE, fp, iface); - write_server_completion_function_body(sig->parameters, fp, iface, sig); - fputs(END_FUNCTION, fp); + /* Emit post-invocation details. */ + + fputs(server_function_body_call, fp); + + /* Free allocated strings. */ + + free(opname); } /* Generate a function body corresponding to an operation for server use. */ -void write_server_wrapper_function_body(struct parameter *param, FILE *fp, +static void write_server_wrapper_function_body(struct parameter *param, FILE *fp, struct interface *iface, struct signature *sig) { int input_words, input_items, output_words, output_items; @@ -191,7 +234,7 @@ /* Generate a function body corresponding to the initiation of an operation. */ -void write_server_initiation_function_body(struct parameter *param, FILE *fp, +static void write_server_initiation_function_body(struct parameter *param, FILE *fp, struct interface *iface, struct signature *sig) { @@ -237,7 +280,7 @@ /* Generate a function body corresponding to the completion of an operation. */ -void write_server_completion_function_body(struct parameter *param, FILE *fp, +static void write_server_completion_function_body(struct parameter *param, FILE *fp, struct interface *iface, struct signature *sig) { @@ -280,96 +323,59 @@ free(opname); } -/* Generate a cast for superfluous message structures. */ + + +/* Public functions. */ + +/* Generate each component operation signature or "function prototype". */ -void write_superfluous_message_cast(FILE *fp, int input_words, int input_items, - int output_words, int output_items) +void write_server_interface_signature(struct signature *sig, + enum signature_role role, FILE *fp, + struct interface *iface) { - if (!input_words && !input_items && !output_words && !output_items) - fputs(server_function_body_unused_message, fp); + write_interface_signature(sig, role, SERVER_ROLE, + get_server_function_role(sig), fp, iface); } -/* Generate the initialisation of input words and items. */ +/* Generate signature declarations for each operation. */ -void write_input_initialisation(struct parameter *param, FILE *fp, - const char *opname, int input_words, - int input_items) +void write_server_signature(struct signature *sig, enum signature_role role, + FILE *fp, struct interface *iface) { - if (input_words) - { - write_accessor_initialisation(IN_PARAMETER, SERVER_ROLE, GENERAL_FUNCTION_ROLE, - opname, fp); - write_message_access(param, SERVER_ROLE, GENERAL_FUNCTION_ROLE, IN_PARAMETER, - WORD_CLASS, fp); - } + if (have_attribute(sig->attributes, "completion")) + write_server_initiation_signature(sig, role, fp, iface); + else + write_server_wrapper_signature(sig, role, fp, iface); - if (input_items) - { - fputs("\n", fp); - write_message_access(param, SERVER_ROLE, GENERAL_FUNCTION_ROLE, IN_PARAMETER, - ITEM_CLASS, fp); - } + /* Write the appropriate completion signature even for use by conventional + operations. */ + + write_server_completion_signature(sig, role, fp, iface); } -/* Generate the initialisation of output words and items. */ +/* Generate function source code for each operation, with the generated function + unpacking a message, calling the actual operation and repacking the + results. */ -void write_output_initialisation(struct parameter *param, FILE *fp, - const char *opname, int output_words, - int output_items, enum function_role function) +void write_server_function(struct signature *sig, FILE *fp, struct interface *iface) { - if (output_words) + if (have_attribute(sig->attributes, "completion")) { - write_accessor_initialisation(OUT_PARAMETER, SERVER_ROLE, function, - opname, fp); - write_message_access(param, SERVER_ROLE, function, OUT_PARAMETER, - WORD_CLASS, fp); + write_server_initiation_signature(sig, DEFINITION_ROLE, fp, iface); + write_server_initiation_function_body(sig->parameters, fp, iface, sig); + fputs(END_FUNCTION, fp); + } + else + { + write_server_wrapper_signature(sig, DEFINITION_ROLE, fp, iface); + write_server_wrapper_function_body(sig->parameters, fp, iface, sig); + fputs(END_FUNCTION, fp); } - if (output_items) - { - fputs("\n", fp); - write_message_access(param, SERVER_ROLE, function, OUT_PARAMETER, - ITEM_CLASS, fp); - } -} - -/* Generate an invocation of the actual server operation. */ - -void write_server_function_call(struct parameter *param, FILE *fp, - struct interface *iface, struct signature *sig, - enum specifier specifier) -{ - int continuing = 0; - char *name, *addr, - *opname = get_signature_operation_name(iface, sig, SERVER_ROLE, - ACCESS_ROLE, conf.language); - - fputs("\n err = ", fp); - - /* Access a method when emitting C++. */ + /* Write the appropriate completion function even for use by conventional + operations. */ - if (conf.language == CPP_LANGUAGE) - fprintf(fp, "_self->%s(", opname); - - /* Employ a function pointer via the object type otherwise. */ - - else - { - fprintf(fp, "_self->iface->%s(_self->ref", opname); - continuing = 1; - } - - /* Generate the parameter list, employing addresses for output parameters. */ - - write_parameters(param, fp, INVOCATION_ROLE, GENERAL_FUNCTION_ROLE, specifier, - continuing); - fputs(");\n", fp); - - /* Emit post-invocation details. */ - - fputs(server_function_body_call, fp); - - /* Free allocated strings. */ - - free(opname); + write_server_completion_signature(sig, DEFINITION_ROLE, fp, iface); + write_server_completion_function_body(sig->parameters, fp, iface, sig); + fputs(END_FUNCTION, fp); } diff -r 7fa8fbaae4c4 -r 170967cc0f8b server.h --- a/server.h Fri Dec 09 19:33:15 2022 +0100 +++ b/server.h Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Server code generation. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -40,46 +40,3 @@ void write_server_interface_signature(struct signature *sig, enum signature_role role, FILE *fp, struct interface *iface); - -void write_server_wrapper_signature(struct signature *sig, - enum signature_role role, - FILE *fp, struct interface *iface); - -void write_server_initiation_signature(struct signature *sig, - enum signature_role role, - FILE *fp, struct interface *iface); - -void write_server_completion_signature(struct signature *sig, - enum signature_role role, - FILE *fp, struct interface *iface); - -/* Wrapper function generation. */ - -void write_server_wrapper_function_body(struct parameter *param, FILE *fp, - struct interface *iface, - struct signature *sig); - -void write_server_initiation_function_body(struct parameter *param, FILE *fp, - struct interface *iface, - struct signature *sig); - -void write_server_completion_function_body(struct parameter *param, FILE *fp, - struct interface *iface, - struct signature *sig); - -/* Common function body code generation. */ - -void write_superfluous_message_cast(FILE *fp, int input_words, int input_items, - int output_words, int output_items); - -void write_input_initialisation(struct parameter *param, FILE *fp, - const char *opname, int input_words, - int input_items); - -void write_output_initialisation(struct parameter *param, FILE *fp, - const char *opname, int output_words, - int output_items, enum function_role function); - -void write_server_function_call(struct parameter *param, FILE *fp, - struct interface *iface, struct signature *sig, - enum specifier specifier); diff -r 7fa8fbaae4c4 -r 170967cc0f8b summary.c --- a/summary.c Fri Dec 09 19:33:15 2022 +0100 +++ b/summary.c Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Interface summary generation. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -22,64 +22,49 @@ #include #include "summary.h" -void show_interface(struct interface *iface) + + +static void indent(int level) { - printf("Interface: %s\n", iface->name); - show_includes(iface->includes); - show_attributes(iface->attributes); - show_signatures(iface->signatures); + while (level--) putchar(' '); } -void show_includes(struct include *inc) +static void show_imports(struct import *imp, int level) +{ + if (imp == NULL) + return; + + show_imports(imp->tail, level); + + indent(level); + printf("%s: %s\n", "Import", imp->filename); +} + +static void show_includes(struct include *inc, int level) { if (inc == NULL) return; - show_includes(inc->tail); - printf("Include: %s\n", inc->filename); -} + + show_includes(inc->tail, level); -void show_signatures(struct signature *sig) -{ - if (sig == NULL) - return; - show_signature(sig); - show_signatures(sig->tail); -} - -void show_signature(struct signature *sig) -{ - printf("Signature: %s %s\n", sig->qualifier, sig->operation); - show_attributes(sig->attributes); - show_parameters(sig->parameters); + indent(level); + printf("%s: %s\n", "Include", inc->filename); } -void show_attributes(struct attribute *attr) +static void show_identifiers(struct identifier *ident) { - if (attr == NULL) - return; - show_attribute(attr); - show_attributes(attr->tail); + while (ident != NULL) + { + printf(" %s", ident->identifier); + ident = ident->tail; + } } -void show_attribute(struct attribute *attr) -{ - printf("Attribute: %s", attr->attribute); - show_identifiers(attr->identifiers); - printf("\n"); -} - -void show_parameters(struct parameter *param) -{ - if (param == NULL) - return; - show_parameter(param); - show_parameters(param->tail); -} - -void show_parameter(struct parameter *param) +static void show_parameter(struct parameter *param, int level) { char *type; + indent(level); printf("Parameter: %s%s", param->specifier & IN_PARAMETER ? "IN" : "", param->specifier & OUT_PARAMETER ? "OUT" : ""); @@ -95,11 +80,86 @@ printf("\n"); } -void show_identifiers(struct identifier *ident) +static void show_parameters(struct parameter *param, int level) +{ + if (param == NULL) + return; + + show_parameter(param, level); + show_parameters(param->tail, level); +} + +static void show_attribute(struct attribute *attr, int level) +{ + indent(level); + printf("Attribute: %s", attr->attribute); + + show_identifiers(attr->identifiers); + printf("\n"); +} + +static void show_attributes(struct attribute *attr, int level) +{ + if (attr == NULL) + return; + + show_attribute(attr, level); + show_attributes(attr->tail, level); +} + +static void show_signature(struct signature *sig, int level) +{ + indent(level); + printf("Signature: %s %s\n", sig->qualifier, sig->operation); + + show_attributes(sig->attributes, level + 2); + show_parameters(sig->parameters, level + 2); +} + +static void show_signatures(struct signature *sig, int level) { - while (ident != NULL) - { - printf(" %s", ident->identifier); - ident = ident->tail; - } + if (sig == NULL) + return; + + show_signature(sig, level); + show_signatures(sig->tail, level); +} + +static void show_bases(struct interface_ref *base, int level) +{ + if (base == NULL) + return; + + indent(level); + printf("Base: %s\n", base->name); + + if (base->iface != NULL) + show_interface(base->iface, level + 2); + show_bases(base->tail, level); } + + + +/* Public functions. */ + +void show_interface(struct interface *iface, int level) +{ + indent(level); + printf("Interface: %s\n", iface->name); + indent(level); + printf("Basename: %s\n", iface->output_basename); + + show_bases(iface->bases, level + 2); + show_imports(iface->imports, level); + show_includes(iface->includes, level); + show_attributes(iface->attributes, level); + show_signatures(iface->signatures, level); +} + +void show_interfaces(struct interface *iface) +{ + if (iface->tail != NULL) + show_interfaces(iface->tail); + + show_interface(iface, 0); +} diff -r 7fa8fbaae4c4 -r 170967cc0f8b summary.h --- a/summary.h Fri Dec 09 19:33:15 2022 +0100 +++ b/summary.h Sat Dec 10 01:28:22 2022 +0100 @@ -1,7 +1,7 @@ /* * Interface summary generation. * - * Copyright (C) 2019 Paul Boddie + * Copyright (C) 2019, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,12 +23,5 @@ #include "types.h" -void show_interface(struct interface *iface); -void show_includes(struct include *inc); -void show_signatures(struct signature *sig); -void show_signature(struct signature *sig); -void show_attributes(struct attribute *attr); -void show_attribute(struct attribute *attr); -void show_parameters(struct parameter *param); -void show_parameter(struct parameter *param); -void show_identifiers(struct identifier *ident); +void show_interface(struct interface *iface, int level); +void show_interfaces(struct interface *iface); diff -r 7fa8fbaae4c4 -r 170967cc0f8b templates.h --- a/templates.h Fri Dec 09 19:33:15 2022 +0100 +++ b/templates.h Sat Dec 10 01:28:22 2022 +0100 @@ -26,13 +26,13 @@ #define client_filename_c "%s/%s_client.c" #define client_filename_cpp "%s/%s_client.cc" #define client_header_filename "%s/%s_client.h" -#define compound_interface_type_filename "%s/%s_interface_type.h" -#define compound_interfaces_filename "%s/%s_interfaces.h" #define interface_filename "%s/%s_interface.h" #define server_filename_c "%s/%s_server.c" #define server_filename_cpp "%s/%s_server.cc" #define server_header_filename "%s/%s_server.h" + + /* Client templates. */ #define client_prologue \ @@ -62,8 +62,8 @@ " : public %s" #define client_interface_endpoint_declaration_cpp \ -"\nprotected:\n" \ -" %s _endp;\n" +"protected:\n" \ +" %s _endp;\n\n" #define client_interface_constructor_cpp \ "\n %s(%s endp) : _endp(endp) { }\n" @@ -71,7 +71,10 @@ /* Client interface definitions for C. */ #define client_interface_declaration_c \ -"\nextern iface_%s client_iface_%s;" +"\nextern iface_%s client_iface_%s;\n" + +#define client_interface_prologue_empty_c \ +"\niface_%s client_iface_%s;" #define client_interface_prologue_c \ "\niface_%s client_iface_%s = {" @@ -82,6 +85,8 @@ #define client_interface_epilogue_c \ "\n};\n" + + /* Compound interface dispatcher templates. */ #define compound_dispatch_prologue \ @@ -92,49 +97,17 @@ " switch (l4_msgtag_label(msg->tag))\n" \ " {\n" -#define compound_dispatch_epilogue \ -" default:\n" \ -" ipc_message_send_error(msg, -L4_EBADPROTO);\n" \ -" ipc_message_reply(msg);\n" \ -" break;\n" \ -" }\n" +/* Compound interface definitions. */ -#define compound_dispatch_header_prologue \ -"#pragma once\n\n" \ -"#include \n" \ +#define compound_interface_include \ "#include \"%s_interface.h\"\n" -/* Compound interface class definitions. */ - -#define compound_interface_prologue_cpp \ -"#pragma once\n\n" \ -"#include \"%s_interfaces.h\"\n\n" \ -"#ifdef __cplusplus\n\n" \ -"class %s :" - -#define compound_interface_prologue_c \ -"#pragma once\n\n" \ -"#include \"%s_interface_type.h\"\n\n" \ -"typedef struct\n" \ -"{\n" - -#define compound_interface_epilogue_cpp \ -"\n{\n};\n\n" \ -"#endif /* __cplusplus */\n" - -#define compound_interface_epilogue_c \ -"\n} iface_%s;\n" - -#define compound_interfaces_prologue \ -"#pragma once\n\n" - #define compound_ref_type_definition_prologue_c \ -"#pragma once\n\n" \ -"#include \n" \ -"#include \"%s_interfaces.h\"\n\n" \ -"typedef union {\n" \ -" %s cap;\n" \ -" void *ptr;\n" +"\ntypedef struct {\n" \ +" union {\n" \ +" %s cap;\n" \ +" void *ptr;\n" \ +" };\n" #define compound_ref_type_definition_member_c \ " ref_%s as_%s;\n" @@ -148,11 +121,44 @@ #define compound_interface_conversion_cpp \ "\n#define convert_to_%s(_self) (_self)\n" + + /* Dispatch functions. */ #define dispatch_function_signature \ "\nvoid dispatch_%s(ipc_message_t *msg, %s *_self)" +/* Dispatch templates. */ + +#define dispatch_function_interface_case \ +" case %s:\n" \ +" dispatch_%s(msg, %s);\n" \ +" break;\n\n" + +#define dispatch_function_wrapper_case \ +" case %s:\n" \ +" ipc_message_send_error(msg, %s_%s(msg, %s));\n" \ +" break;\n\n" + +#define dispatch_function_reply_wrapper_case \ +" case %s:\n" \ +" err = %s_%s(msg, %s);\n" \ +" ipc_message_send_error(msg, err != IPC_MESSAGE_SENT ? err : (long) L4_EOK);\n" \ +" if (err != IPC_MESSAGE_SENT)\n" \ +" ipc_message_reply(msg);\n" \ +" break;\n\n" + +#define dispatch_function_default_case \ +" default:\n" \ +" ipc_message_send_error(msg, -L4_EBADPROTO);\n" \ +" ipc_message_reply(msg);\n" \ +" break;\n" + +#define dispatch_function_dispatcher_epilogue \ +" }\n" + + + /* Message handling functions. */ #define handle_function_signature \ @@ -163,6 +169,8 @@ " dispatch_%s(msg, _self);\n" \ " ipc_message_discard(msg);\n" + + /* General header template. */ #define header_prologue \ @@ -170,6 +178,8 @@ "#include \n" \ "#include \n" + + /* Server templates. */ #define server_header_prologue \ @@ -226,14 +236,7 @@ "\n ipc_message_prepare(msg);\n\n" \ " return L4_EOK;\n" -/* Server dispatch function templates. */ -#define server_function_dispatcher_body_epilogue \ -" default:\n" \ -" ipc_message_send_error(msg, -L4_EBADPROTO);\n" \ -" ipc_message_reply(msg);\n" \ -" break;\n" \ -" }\n" /* Interface class definitions. */ @@ -241,14 +244,20 @@ "\n#ifdef __cplusplus\n" \ "\nclass %s" +#define interface_prologue_base_cpp \ +"%spublic %s" + #define interface_prologue_c \ "\ntypedef struct" \ #define interface_body_begin \ -"\n{" +"\n{\n" + +#define interface_body_base_c \ +" iface_%s *to_%s;\n" #define interface_signatures_prologue_cpp \ -"\npublic:" +"public:" #define interface_epilogue_cpp \ "};\n\n" \ @@ -261,7 +270,7 @@ "\n%slong %s(" #define interface_struct_member_function_signature_prologue \ -"\n long (*%s)(" +" long (*%s)(" #define interface_unimplemented_method_prologue_cpp \ "\n {" @@ -287,6 +296,8 @@ #define expected_items_definition \ "\n#define %s_expected_items %d\n" + + /* Opcode definitions. */ #define opcode_enumeration_prologue \ @@ -323,25 +334,7 @@ #define message_accessor_writing_initialiser \ " ipc_message_reserve_words(%smsg, sizeof(struct %s_words_%s));\n" -/* Dispatch templates. */ -#define dispatch_function_interface_case \ -" case %s:\n" \ -" dispatch_%s(msg, %s);\n" \ -" break;\n\n" - -#define dispatch_function_wrapper_case \ -" case %s:\n" \ -" ipc_message_send_error(msg, %s_%s(msg, %s));\n" \ -" break;\n\n" - -#define dispatch_function_reply_wrapper_case \ -" case %s:\n" \ -" err = %s_%s(msg, %s);\n" \ -" ipc_message_send_error(msg, err != IPC_MESSAGE_SENT ? err : (long) L4_EOK);\n" \ -" if (err != IPC_MESSAGE_SENT)\n" \ -" ipc_message_reply(msg);\n" \ -" break;\n\n" /* Tokens. */ @@ -354,6 +347,8 @@ #define COMPLETE_SIGNATURE ";\n" #define END_SIGNATURE "\n" + + /* Texts. */ #define help_text \ @@ -362,14 +357,13 @@ "The following options are supported:\n\n" \ "--all or -a Produce all kinds of output for a given language.\n\n" \ "--client or -c Generate client code.\n\n" \ -"--comp or -C Generate a compound/composite/component interface using\n" \ -" the given filename prefix.\n\n" \ "--dir or -d Create output in the indicated directory.\n\n" \ +"--files or -f Only show processed filenames, produce no files.\n\n" \ "--headers or -h Produce header files.\n\n" \ "--help or -? Show this message.\n\n" \ "--interfaces or -i Produce interface header files.\n\n" \ "--language or -l Generate code in the indicated language.\n\n" \ -"--comp-name or -N Indicate the compound/composite/component interface name.\n\n" \ +"--recursive or -R Also generate code for any base interfaces.\n\n" \ "--routines or -r Produce program routines.\n\n" \ "--server or -s Generate server code.\n\n" \ "--verbose or -v Show verbose output reporting the processed interfaces.\n\n" \ diff -r 7fa8fbaae4c4 -r 170967cc0f8b types.h --- a/types.h Fri Dec 09 19:33:15 2022 +0100 +++ b/types.h Sat Dec 10 01:28:22 2022 +0100 @@ -98,11 +98,21 @@ CPP_LANGUAGE, }; +/* Import declarations. */ + +struct import +{ + char *filename; + struct interface *iface; + struct import *tail; +}; + /* Include declarations. */ struct include { char *filename; + struct interface *iface; struct include *tail; }; @@ -144,13 +154,25 @@ struct attribute *tail; }; +/* A reference to an interface linking to other references. */ + +struct interface_ref +{ + char *name; + struct interface *iface; + struct interface_ref *tail; +}; + /* A single interface description linking to other interfaces. */ struct interface { char *name; + const char *output_dirname, *output_basename; + struct interface_ref *bases; struct signature *signatures; struct attribute *attributes; + struct import *imports; struct include *includes; struct interface *tail; };