]> bicyclesonthemoon.info Git - botm/config/commitdiff
More consistent behavior for undefined.
authorb <rowerynaksiezycu@gmail.com>
Sat, 10 Sep 2022 20:49:31 +0000 (20:49 +0000)
committerb <rowerynaksiezycu@gmail.com>
Sat, 10 Sep 2022 20:49:31 +0000 (20:49 +0000)
Added explanation.

configure.pl

index 1caa8133fc915c50ae618210fa1155a77a9d7cf2..499419f455c51c3d5864accf0c2a77a9a9b794ff 100755 (executable)
@@ -1,7 +1,6 @@
 #!/usr/bin/perl
 
 # configure.pl
-
 # The new BOTM configuration tool
 
 #    Copyright (C) 2022  Balthasar SzczepaƄski
 #    You should have received a copy of the GNU Affero General Public License
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+# This is a tool for managing configuration in a project.
+# It was initially based on my configuration script from the BSTA software
+# but then I have rewriten it from beginning with new design goals.
+#
+# The goals:
+#  - tool to read confinguration files and then apply changes in
+#    project's source files
+#  - configure the project at compile time, easy cooperation with makefile
+#  - possible to keep separately:
+#     - the actual important settins
+#     - the settings derived from them
+#     - the configuration tool
+#     - the files to configure
+#
+# Why:
+#
+# 1. You write a program. You are silently making assumptions. Whenever
+#    you need a path or other dependency, you define ("hard-code") it there.
+#    Acceptable for a simple, short, single-use program maybe.
+#    But terrible for anything more serious. Maintenance and portability
+#    becomes difficult. When you want to change anything you have to look
+#    through the whole code to find all places where something is defined.
+#    A lot of effort and easy to make a mistake.
+#
+# 2. Instead of hard-coding everything everywhere you can define the assumed
+#    settings at the begnning of each file using 'const' or '#define' or
+#    whatever the programming language allows. Even better if you can put the
+#    definitions into a single file and include them where needed.
+#    Much better but still for some big projects with many files it takes some
+#    time and effort to go through all of them. Even if all you wanted was
+#    to adapt the project to run on a different system for example.
+#
+# 3. Ok, you could have all definitions in one place and then apply them
+#    to all the files. One way to do it is to make a script which will read
+#    the settings from a file and then insert '#define' (C), 'use constant'
+#    (Perl), or other statements into the files based on pattern matching.
+#
+#    This is of for configuration done at compile time. Not at run time.
+#    There the program itself has to read the settings and make decisions.
+#
+#    There also exist things like autoconf, automake, etc. see:
+#    http://www.mrob.com/pub/comp/unix-building-history.html
+#    However for many project I'm currently dealing with this script
+#    here is totally enough and so far I did not have to learn these tools.
+#    They are still a black box (or even black magic) to me.
+#
+# Usage:
+#
+# perl configure.pl [configfile1 configfile2 ...] < input_file > output_file
+#
+# Or './configure.pl' instead of 'perl configure.pl' but then we're making
+# the assumption that Perl is installed at '/usr/bin/perl'.
+# Of course we can make configure.pl conrigurable by configure.pl and then
+# make it reconfigure itself to adapt to where Perl is but I decided to not
+# go  there. Instead I can handle this on the level of the makefile (which
+# is configurable by configure.pl)
+#
+# The script reads configuration from all files given in the command line
+# parameters. The effect is similar to reading a single concatenated file.
+# 
+# Then it reads standard input line by line.
+# In each line it looks for replacement patterns, replaces them if found,
+# and prints the line to the output.
+#
+# Configuration files are read once in the same order as the command line
+# parameters.
+#
+# Whenever I write 'whitespace' I mean it can space '' or TAB '\t'.
+#
+# It is possible to include files like this:
+#   include path_to_include
+# The include statement must start from the first column in the line.
+# It is equivalent to replacing the line (with the include statement)
+# by the content of the included file
+#
+# settings can be defined in two ways:
+#   name: value
+#   name = value
+# These must start from the first column in the line.
+# If a line starts with a whitespace it is treated as a continuation
+# of the value:
+#   name: line1
+#    line2
+#   name = line1
+#          line2
+# The name consists of the characters A-Z, a-z, 0-9, '-', '_', and '.'.
+#
+# In the first format (name: value) the ':' must be immediately after the name.
+# The value starts immediately after the first whitespace and is taken directly
+# without any processing.
+#
+# In the second format (name = value) some processing is performed:
+#   - Any amount (including 0) of whitespace is allowed before and
+#     after the '+'.
+#   - Comments starting from '#' are removed.
+#   - whitespace at beginning and end of each line are removed
+#   - escaped characters (by '\') are processed.
+#   - other settings inserted by $name are expanded.
+#
+# It is possible to insert one setting's value into another one like this:
+#   s1: 456
+#   s2 = 123 $s1 789
+# In this example the value of s2 is '123 456 789'.
+# This will not work:
+#   s2 = 123 $s1 789
+#   s1: 456
+# When processing s2 $s1 is not defined yet so the value of s2 is:
+# '123   789'.
+#
+# It is possible to use a value as a function pattern:
+#   s1: $0 = "$1"
+#   s2 = $s1(a,b)
+# In this example the value of s2 is: 'a = "b"'.
+# Here names made entirely from digits are used as the parameters of the 
+# function pattern and should not be used for other purposes. Possible but
+# not recommended. another special name is $_, which joins all parameters
+# by ','.
+#
+# If not provided, the parameters will be treated as empty string.
+# So here:
+#   s1: $0 = "$1"
+#   s2 = $s1(a)
+# the value of s2 is: 'a = ""'.
+#
+# Even though $0 and $1 were not processed when s1 was defined, they
+# were processed when s1 was used as a function pattern.
+#
+# In this example:
+#   s1 = $0 = "$1"
+#   s2 = $s1(a,b)
+# s1 will be immediately evaluated to ' = ""', and s2 will also become
+# ' = ""'.
+#
+# Escaping will protece $0 and $2 to be processed too early:
+#   s1 = \$0 = "\$1"
+#   s2 = $s1(a,b)
+# Here the value of s1 is '$0 = "$1"' again.
+#
+# The pattern function calls can be nested:
+#   s1: >$0<
+#   s2: <$0>
+#   s3 = $s2($s1(1))
+# In this example this is how it will be processed:
+#   processing s3, '$s2($s1(1))'
+#   found call to s2 with parameters '($s1(1))'
+#     processing s2, '<$0>', '($s1(1))'
+#       processing parameters '($s1(1))'
+#       found parameter '$s1(1)'
+#         processing '$s1(1)'
+#         found call to s1 with parameters '(1)'
+#           processing s1, '>$0<', '(1)'
+#             processing parameters '(1)'
+#             found parameter '1'
+#               processing '1'
+#               return '1'
+#             value is now '1'
+#             return ('1')
+#           found call to $0 with no parameters
+#           replace $0 with '1'
+#           value is now '>1<'
+#           return '>1<'
+#         replace $s1() with '>1<'
+#         value is now '>1<'
+#         return '>1<'
+#       value is now '>1<'
+#       return ('>1<')
+#     found call to $0 with no parameters
+#     replace $0 with '>1<'
+#     value is now '<>1<>'
+#     return '<>1<>'
+#   replace $2() with '<>1<>'
+#   value is now '<>1<>'
+#   return '<>1<>'
+#
+# So when processing a value, if a pattern function call is found,
+# then first, the parameters are separated, and each is processed one
+# recursion level deeper.
+# then the pattern is processed (also one recursion level deeper than the
+# base value) and the $0, etc. are replaced by the (already processed)
+# parameters.
+#
+# Empty parameter list can be useful for separating from text afterwards:
+#   s1: 456
+#   s2 = 123$s1()789
+# Here the value of s2 is: '123456789'.
+# Without the '()':
+#      s1: 456
+#   s2 = 123$s1789
+#  Here the value of s2 is: '123' because $s1789 was not found.
+#
+# Escaping the '(' prevents interpreting it as some parameters:
+#   s1: 456
+#   s2 = (123)$s1\(789)
+# Here the value of s2 is: '(123)456(789)'.
+# Without the '\(':
+#   s1: 456
+#   s2 = (123)$s1(789)
+# Here the value of s2 is: '(123)456' because '789' was given to s1
+# as $0 and ignored.
+#
+# There are special functions and values:
+#   $_ESCAPE is a function which will escape some characters with '\'.
+#     $0 - the text to be escaped
+#     $1 - the regexp which specifies which characters should be escaped
+#          if not specified then a default regexp is used
+#   $_URL_ENCODE is a function which will perform percent-encoding
+#     $0 - the text to be encoded
+#     $1 - the regexp which specifies which characters should be encoded
+#          if not specified then a default regexp is used
+#   $_SPRINTF is a call to the Perl sprintf() function
+#     $0 - the format string
+#     $1 and continuing - the parameters to sprintf()
+#   $_PATH is a function which will join a path from segments
+#     $0 and continuing - the segments to be joined
+#   $_PATH_SEPATATOR is the path separator used by $_PATH.
+#     can be overwritten, default value '/'.
+#   $_REPLACE_LINE is the pattern to look for in files, to replace the 
+#     entire line. Can be overwritten. Default value '###$0:'
+#     $0 - the name of the setting
+#   $REPLACE_KEYWORD is the pattern to look for in files, to replace
+#     just the pattern. Can be overwritten. Default value '###$0;'
+#     $0 - the name of the setting
+#   More functions and values can be added in future versions.
+#
+# Inserting settings to source files:
+#
+# After all settings are read, the actual source is processed line by line.
+# In each line the tool will look for patterns to replace the entire line.
+# The patterns are built by inserting settings names to $REPLACE_LINE.
+# For example if:
+#   REPLACE_LINE: ###$0:
+#   s1: ABC
+# then each line containing '###s1:' will be replaced by 'ABC', and so on.
+# The patterns are checked in unpredictable order (because Perl hash).
+# If any match is found, the line is replaced and the tool moves to next line.
+# If none of the line patterns are matched then comes next step:
+# The tool will now look for patterns to replace in the line.
+# The patterns are built by inserting settings names to $REPLACE_KEYWORD.
+# For example if:
+#   REPLACE_KEYWORD: ###$0;
+#   s3: XYZ
+# then each occurence of '###s3:' will be replaced by 'XYZ', and so on.
+# The patterns are checked in unpredictable order (because Perl hash)
+#
+# Use in project:
+# 
+# Either install the tool somewhere on the system and multiple projects
+# can call it, or copy the tool as part of the project
+#
+# See the makefile of the tool's project as a simple example.
+#
+# Of course if you found this tool as part of a different project then
+# you probably will not see the original makefile.
+# Instead maybe check how the tool is used in the project.
+#
+# Some ideas:
+# 
+# It's a good idea to use the tool together with the makefile, to compile
+# the project.
+#
+# Generate source files and then use them:
+#
+#   abc: abc.c
+#      gcc -o abc abc.c
+#   
+#   abc.c: abc.1.c settings.txt configure.pl
+#      perl configure.pl settings.txt < abd.1.c > abc.c
+#
+# Of course not only the source files but also the makefile itself can be
+# configured:
+# 
+#   makefile: makefile.1.mak settings.txt configure.pl
+#      perl configure.pl settings.txt < makefile.1.mak > makefile
+# 
+# If the settings have changed then any attept to make something will
+# first regenerate the makefile and then use it.
+#
+# Use different settings for different targets:
+#
+#   ifndef TARGET
+#   TARGET = debug
+#   endif
+#   
+#   SETTINGS = settings-$(TARGET).txt settings.txt
+#   
+#   makefile: makefile.1.mak $(SETTINGS) configure.pl
+#      perl configure.pl $(SETTINGS) < makefile.1.mak > makefile
+#
+# Then use:
+#   make TARGET=targetname something_to_make
+#
+# Or even replace the default target by a configurable pattern.
+# then, to set a new target use:
+#   make TARGET=targetname -B makefile
+# And afterwards the new tatget will be the default one.
+
 use strict;
 
 use constant MAX_DEPTH               => 256;
@@ -297,6 +592,7 @@ sub parse_pattern {
        
        if ($depth >= MAX_DEPTH) {
                return '$'.$name.$parameters;
+               # return '';
        }
        
        my @parameter_list = parse_pattern_parameters($parameters, $depth, %cfg);
@@ -343,6 +639,9 @@ sub parse_pattern {
                }
                return $parsed;
        }
+       # elsif ($name =~ /^[0-9+]$/) {
+               # return '';
+       # }
        elsif ($name eq ESCAPE) {
                return escape(@parameter_list);
        }
@@ -357,7 +656,8 @@ sub parse_pattern {
                return sprintf($format,@parameter_list);
        }
        else {
-               return '$'.$name.$parameters;
+               # return '$'.$name.$parameters;
+               return '';
        }
 }