As you may know over the past few months I have been doing some work on FSharpChart. One of the source code changes that has been implemented over this time is the breakdown of the original single FSX script file into more manageable FS source files. However in doing this I was left with the need to compose the individual FS source files back into a single FSX script file; as folks may still wish to use this single file. Rather than do this composition manually for each release I decided to write a little utility script to achieve this.
In writing this code to generate the composite script file I wanted to do achieve several things:
- Allow one to use a template to manage inclusion of reference and items like fsi.AddPrinter
- Allow for the management of the namespaces and open statements
- Easily allow for the inclusion of a multiple FS source files
For generating the FSharpChart FSX script file the template source I used is as follows:
The premise of this template file is that it is used to generate the FSX file where each #file tag is expanded to include the contents of the specified source file; where namespaces and open statements are modified to be consistent with the namespace of the template file.
In this example the template file defines the default namespace as “MSDN.FSharp.Charting”. As such when writing out the contents of a source file the base namespace element needed to be removed and the open statements modified to reflect this change. Thus in this case, with the default namespace being “MSDN.FSharp.Charting”, any open statement with this prefix needs to be transformed to exclude the default value; namely:
needs to be transformed to:
With this in mind here is the transform code:
So what does the script do?
The templateFile list, which is written as the single FSX script file, is generated by one of two lines. Either the template source line is written directly to the output file or a #file tag is matched (MatchFileInclude) and the contents are processed.
In processing a code file firstly the open statements are written, through the openFileLines function, and secondly the remaining code lines are written, using the codeFileLines function. When executing these functions, processing occurs according to a the determination of when all the open statements have all been read and the first code line has been encountered. In this case the determination is made of this point in the source code through the regular expression match of a module, type or attribute statement.
Most of the code inspection is done through regular expressions. A file tag is located using an Active Pattern and the RegEx pattern:
This way one can easily locate a #file tag and extract the source filename to be included. A similar mechanism is used for open statements where the captured expression is the new open statement value.
For the determination of the start of the code the RegEx pattern that is used for locating a module, type or attribute statement is:
This code is a little specialized, as it does depend on some well structured source code file, but hopefully it does demonstrate that one can easily manage separate source code file, yet if necessary still easily deliver a single script file.