trick/docs/developer_docs/DesConvertSwig.md
Pherring04 a9aa7088ad
SWIG 4 Template Directive Changes (#1741)
* Updated convert_swig to account for template directive changes in SWIG 4

* Fixed merge issues

* Merge cleanup
2024-07-23 11:45:37 -05:00

13 KiB

HomeDeveloper Docs → Convert Swig

convert_swig

The purpose of convert_swig is to create SWIG interface files for the given C/C++ header file (usually S_source.hh) and each of the header files that it (recursively) includes. SWIG (Simplified Wrapper and Interface Generator) is an interface compiler that connects programs written in C and C++ with scripting languagues such as Perl and Python.

In Trick the list of header files to be processed is usually produced by the script make_swig_makefile.pm, as it's creating Makefile_swig. This list is stored in the file ".S_library_swig". So, if .S_library_swig exists, we can just open and read it.

Otherwise we need to process S_source.hh to produce the list of header files.

Specifically, we want to generate SWIG interfaces for those header files that are:

  1. actual dependencies of S_source.hh, GIVEN THE CURRENT environment and
  2. not excluded from ICG processing ( by ICG_NO or ICG_EXCLUDE).

The header files that are actually included are the dependencies we care about. Keep in mind that the preprocessor and the current ENVIRONMENT may cause some headers to be conditionally included or excluded. We only want to generate SWIG interfaces for headers that are ACTUALLY included.

Whereas the preprocessor can (using the gcc -MM option) generate a list of dependencies that satisfy 1) (above), it can't handle that ICG exclusions. And, whereas the function get_headers() can generate a list of dependences which are flagged if they contain ICG_NO, it doesn't handle conditional includes.

So, the strategy that we employ is to generate and then find the intersection of both lists. Then we eliminate those that are in 1) $TRICK_HOME/trick_source, or 2) flagged as containing ICG_NO or 3) are in ICG_EXCLUDE'd directories.

First, create a list headers using the GCC with the -MM option. GCC will handle conditional inclusion.

Second, create a list where the files are flagged if they contain ICG_NO.

Then we generate the intersection of the two lists and then eliminate the dependencies that:

  1. are in $TRICK_HOME/trick_source.
  2. contain ICG_NO.
  3. are in ICG_EXCLUDE'd directories.

to create the final list of header dependencies that we need to convert into SWIG interfaces.

Next we need to determine which of the files do not have up-to-date SWIG files. For each header file in final dependency list, if the corresponding SWIG (.i) file doesn't exist or the header file is newer than the existing SWIG file, then record that a new SWIG file needs needs to be created. The global hash %out_of_date represents a list of header files whose corresponding .i files need to be regenerated.

Finally, call process_file() to create SWIG interface files for each of the out_of_date headers.

process_file

Synopsis

This subroutine processes S_source.h and each of it's requisite header files to generate the corresponding SWIG interfaces files.

Parameters

sim_ref Is this parameter ever used?

in_file The name of input file, invariably "S_source.hh".

Design

This function first reads the contents of <in_file> and each of the header files that it (recursively) includes into a hash (%file_contents), keyed by the corresponding filenames. It then converts the contents of the each header whose corresponding SWIG interface file is out of date into a SWIG interface file.

Creating the %file_contents hash

To create the file_contents hash, we first run the input file through the C/C++ preprocessor with the -dI option. This creates one file containing the contents of all of the included header files delimited by "line markers". The line markers indicate which header file each of content came from. Preprocessor line markers are of the form: '#' They are described in Chapter 9, "Preprocessor Output" of the GCC document, "Preprocessor Output - The C Preprocessor". Whether in_file is run through the C or C++ preprocessor depends on its name suffix.

For each line in the ONE big file, check whether it's a linemarker or not. if it's a linemarker ( telling us where the following content is from) extract the header filename. This will be our current filecontents hash key. If it's not a linemarker, then it must be content. So, append it to the string, whose key is the current file name ($curr_name).

Creating SWIG interface files

The global hash %out_of_date represents the list of header files whose corresponding SWIG interface files are out of date. It is generated in the main part of the convert_swig program.

For each of these out of date header files, we generate a SWIG interface file from the contents stored in the %file_contents hash.

First we remove the friend init_attr functions from the headers content. They don't need to be wrapped.

Then, for each of the #includes in the out_of_date header file create a corresponding %import directive.

Next, we generate a module name and path for the SWIG interface file. The module name is generated from an md5 hash of the header file's full name.

Finally we open the SWIG interface file, and in it we:

  1. write a %module directive that identifies the module.
  2. write a #include directive to include trick_swig interface utilities.
  3. write a #include directive to include the header file to which this interface file corresponds and from which it was derived.
  4. create a SWIG interface for each class declared in the corresponding header file using the %trick_swig_class_typemap() macro. This macro is defined in swig_class_typedef.i, included by trick_swig.i (see above).
  5. Write the SWIG interface code (processed header file) and the header file contents.

process_contents

Synopsis

Process header file contents for use in the corresponding SWIG interface file.

Parameters

contents_ref (IN) reference to header file contents that are to be converted to a SWIG interface.

new_contents_ref (OUT) SWIG interface code, derived from the header file contents.

curr_namespace (IN) current namespace.

class_names_ref (OUT) reference to an array of class and/or struct names encountered when processing the header file contents.

template_typedefs_ref (OUT) Series of SWIG %template directives. %template directives create a type in the target language that corresponds to a C++ template instance.

Description

While there's header file content remaining to be processed, repeatedly make the best match with the following available patterns:

Case of :
typedef existing-type-name new-type-name ';'
Concatenate the matched text to the SWIG interface text.

Case of :
typedef enum optional-name '{' bracketed-content '}' enum-name';'
Concatenate the matched text to the SWIG interface text.

Case of :
typedef ( struct | union ) name '{'
Call process_typedef_struct() to process the matched text and the bracketed content of the struct that follows in the header file contents and update the SWIG interface text accordingly.

Case of :
template '<' template-parameters '>' class class-name
then just concatenate the matched text to the SWIG interface text.

Case of:
namespace name
then call process_namespace() to process the matched text and the bracketed content that follows in the header file contents and update the the SWIG interface text accordingly.

Case of:
( class | struct ) class-name ( '{' | ':' )
Call process_class() to process the matched text and the bracketed that follows in the header file contents and update the the SWIG interface text.

Default: Match anything that doesn't match the other patterns and concatenate it to the to the SWIG interface text. Note that (in Perl) ? in the regular expression (.?) is a non-greedy quantifier, so it gobbles up text only until another match can be made.

process_namespace

Synopsis

Process namespaces found in a header file for use in the corresponding SWIG interface file.

Parameters

namespace_string (IN) This is a string of the form: namespace name, that was extracted from the header file contents. In the contents there should remain the bracketed content to which this namespace applies.

contents_ref (IN) This is a reference to the remainder of the header file (following the above string) to be processed.

new_contents_ref (OUT) The SWIG code generated so far.

curr_namespace (IN) current namespace.

class_names_ref (OUT) reference to an array of class and/or struct names encountered when processing the header file contents.

template_typedefs_ref (OUT) Series of SWIG %template directives. %template directives create a type in the target language that corresponds to a C++ template instance.

Description

Extract the name from the namespace_string and append it to the current namespace's name.

Add the namespace_string to the SWIG interface text.

Call extract_bracketed() to extract the contents of the namespace from the header text.

Call process_contents() to convert the extracted namespace contents to a SWIG interface.

Append whatever wasn't matched in process contents to the SWIG interface text.

process_class

Synopsis

Process classes declarations found in a header file for use in the corresponding SWIG interface file.

Parameters

class_string (IN) This is a string of the form:
( class | struct ) class-name ( '{' | ':' )

contents_ref (IN) This is a reference to the remainder of the header file (following the class_string) to be processed.

new_contents_ref (OUT) The SWIG code generated so far.

curr_namespace (IN) current namespace.

class_names_ref (OUT) reference to an array of class and/or struct names encountered when processing the header file contents.

template_typedefs_ref (OUT) Series of SWIG %template directives. %template directives create a type in the target language that corresponds to a C++ template instance.

Description

process_class() processes class declarations with the following steps:

  1. Extract the class_name from the class_string. Add the class_string to the SWIG interface text.

  2. Call extract_bracketed() to extract the class contents between '{' and '}'.

  3. While there's class content text remaining to be processed, repeatedly search for data members that match : template_name '<' template-params '>' name ; For each match, create a SWIG %template directive to create an instantiation of the specific templated type used by the data member. Due to changes in SWIG 4, template directives must be specified before their respective data members. As such, the template directives are inserted immediately prior to the class definition. SWIG does not resolve namespaces in these directives the same as C++ does (it will only check local scope, not global). To account for this, if the directive specifies a template outside the local namespace, convert_swig will escape the current namespace in the format : } template_directive namespace name {

process_typedef_struct

Synopsis

Process a type definition of a struct or union to make it suitable as SWIG interface code. Extract the struct (or union) name and bracketed contents from the header file text (typedef_struct_string and contents_ref) . Record the extracted names in the list referenced by class_names_ref, and then reconsistute the type definition, via the new_contents_ref.

Parameters

typedef_struct_string (IN) This is a string of the form:

 1. <b>typedef struct</b> [ <i>optional-name</i> ] <b>"{"</b>   OR<br>
 1. <b>typedef union</b> [ <i>optional-name</i> ] <b>"{"</b><br>

contents_ref (IN) This is a reference to the remainder of the header file (following the above string) to be processed.

new_contents_ref (OUT) The SWIG interface code generated so far.

class_names_ref (OUT) reference to an array of class and/or struct names encountered when processing the header file contents.

Description

process_typedef_struct() processes a type definition of a struct or union with the following steps:

  1. Append the typedef_struct_string to the SWIG interface text (via new_contents_ref).

  2. Extract the optional-name from the typedef_struct_string.

  3. Call extract_bracketed() to extract the struct contents from the header text (via contents_ref), including the right bracket.

  4. Extract the one or more typedef'ed names followed by a semi-colon, that should still be in the header text.

  5. Push the optional-name and the typedef'ed names into the class_names list (via class_names_ref).

  6. Append the bracketed struct contents and the one or more typedef'ed names and the semi-colon that we just extracted to the SWIG interface text.