#!/bin/awk -f # scripts/options.awk - library build configuration control # # last changed in libpng version 1.5.0 - April 25, 2010 # # Copyright (c) 1998-2010 Glenn Randers-Pehrson # # This code is released under the libpng license. # For conditions of distribution and use, see the disclaimer # and license in png.h # The output of this script is written to the file given by # the variable 'out'. The script is run twice, once with # an intermediate output file, 'options.tmp' then again on # that file to produce the final output. Each time the # output files and whether or not to pre-prrocess must be # specified: # # awk -f scripts/options.awk pre=1 out=options.tmp scripts/options.dat 1>&2 # awk -f scripts/options.awk pre=0 out=options.dfn options.tmp 1>&2 BEGIN{ out="/dev/null" # intermediate, preprocessed, file pre=-1 # preprocess start="PNG_DEFN_MAGIC-" # Arbitrary start end="-PNG_DEFN_END" # Arbitrary end cx= "/@@@*" # Open C comment for output file comment=start cx # Comment start cend="*/" end # Comment end def=start "#define PNG_@@@" # Arbitrary define sup="@@@_SUPPORTED" end # end supported option und=comment "#undef PNG_@@@" # Unsupported option une="@@@_SUPPORTED" cend # end unsupported option error=start "ERROR:" # error message deb=0 # debug - set on command line logunsupported=0 # write unsupported options too # Precreate arrays option[""] = "" # list of all options: default on/off/"" done[""] = 1 # marks option as having been output requires[""] = "" # requires by option iffs[""] = "" # if by option enabledby[""] = "" # options that enable it by option setting[""] = "" # requires by setting defaults[""] = "" # used for a defaulted value doneset[""] = 1 # marks setting as having been output r[""] = "" # Temporary array # For decorating the output file protect = "" } # Preprocessing - this just copies the input file with lines # that need preprocessing (just chunk at present) expanded pre && $1 != "chunk"{ print >out next } # The first characters of the line determine how it is processed, # leading spaces are ignored. In general tokens that are not # keywords are the names of options. An option 'name' is # controlled by the definition of the corresponding macros: # # PNG_name_SUPPORTED The option is turned on # PNG_NO_name # PNG_NO_name_SUPPORTED If the first macro is not defined # either of these will turn the option off # # If none of these macros are defined the option is turned on, unless # the keyword 'off' is given in a line relating to the option. The # keyword 'on' can also be given, but it will be ignored (since it is # the default.) # # In the syntax below a 'name' is indicated by "NAME", other macro # values are indicated by "MACRO", as with "NAME" the leading "PNG_" # is omitted, but in this case the "NO_" prefix and the "_SUPPORTED" # suffix are never used. # # Each line is introduced by a keyword - the first non-space characters # on the line. A line starting with a '#' is a comment - it is totally # ignored. Keywords are as follows, a NAME, is simply a macro name # without the leading PNG_, PNG_NO_ or the trailing _SUPPORTED. $1 ~ /^#/ || $0 ~ /^[ ]*$/{ next } # com # The whole line is placed in the output file as a comment with # the preceding 'com' removed $1 == "com"{ if (NF > 1) { sub(/^[ ]*com[ ]*/, "") print comment, $0, cend >out } else print start end >out next } # file output input protect # Informational: the official name of the input file (without # make generated local directories), the official name of the # output file and, if required, a name to use in a protection # macro for the contents. $1 == "file" && NF >= 2{ print comment, $2, cend >out print comment, "Machine generated file: DO NOT EDIT", cend >out if (NF >= 3) print comment, "Derived from:", $3, cend >out protect = $4 if (protect != "") { print start "#ifndef", protect end >out print start "#define", protect end >out } next } # option NAME ( (requires|enables|if) NAME* | on | off )* # Declares an option 'NAME' and describes its default setting (on|off) # and its relationship to other options. The option is disabled # unless *all* the options listed after 'requires' are set and at # least one of the options listed after 'if' is set. If the # option is set then it turns on all the options listed after 'enables'. $1 == "option" && NF >= 2{ onoff = option[$2] # records default on or default off key = "" for (i=3; i<=NF; ++i) { if ($(i) == "on" || $(i) == "off") { key = "" if (onoff != $(i)) { if (onoff == "") onoff = $(i) else { # Print a message, otherwise the error # below is incomprehensible print $2 ": currently", onoff ": attempt to turn", $(i) break } } } else if ($(i) == "requires" || $(i) == "if" || $(i) == "enables") { key = $(i) } else if (key == "requires") { requires[$2] = requires[$2] " " $(i) } else if (key == "if") { iffs[$2] = iffs[$2] " " $(i) } else if (key == "enables") { enabledby[$(i)] = enabledby[$(i)] " " $2 } else break # bad line format } if (i > NF) { # Set the option - this may explicitly set it to "", that's good # becaue we need to ensure it exists and we can't rely on some # actually quite sensible awk implementation not optimising the # read above away (gawk doesn't) option[$2] = onoff next } # Else fall through to the error handler } # chunk NAME [requires OPT] [off] # Expands to the 'option' settings appropriate to the reading and # writing of an ancilliary PNG chunk 'NAME': # # option READ_NAME requires READ_ANCILLARY_CHUNKS [READ_OPT] # option READ_NAME enables NAME # [option READ_NAME off] # option WRITE_NAME requires WRITE_ANCILLARY_CHUNKS [WRITE_OPT] # option WRITE_NAME enables NAME # [option WRITE_NAME off] pre && $1 == "chunk" && NF >= 2{ # 'chunk' is handled on the first pass by writing appropriate # 'option' lines into the intermediate file. onoff = "" reqread = "" reqwrite = "" i = 3 # indicates format error if (NF > 2) { # read the keywords/additional OPTS req = 0 for (i=3; i<=NF; ++i) { if ($(i) == "on" || $(i) == "off") { if (onoff != $(i)) { if (onoff == "") onoff = $(i) else break # on/off conflict } } else if ($(i) == "requires") req = 1 else if (req != 1) break # bad line: handled below else { reqread = reqread " READ_" $(i) reqwrite = reqwrite " WRITE_" $(i) } } } if (i > NF) { # Output new 'option' lines to the intermediate file (out) print "option READ_" $2, "requires READ_ANCILLARY_CHUNKS" reqread, "enables", $2, onoff >out print "option WRITE_" $2, "requires WRITE_ANCILLARY_CHUNKS" reqwrite, "enables", $2, onoff >out next } # Else hit the error handler below - bad line format! } # setting MACRO ( requires MACRO* )* [ default VALUE ] # Behaves in a similar way to 'option' without looking for NO_ or # _SUPPORTED; the macro is enabled if it is defined so long as all # the 'requires' macros are also defined. The definitions may be # empty, an error will be issued if the 'requires' macros are # *not* defined. If given the 'default' value is used if the # macro is not defined. The default value will be re-tokenised. # (BTW: this is somewhat restrictive, it mainly exists for the # support of non-standard configurations and numeric parameters, # see the uses in scripts/options.dat $1 == "setting" && (NF == 2 || NF >= 3 && ($3 == "requires" || $3 == "default")){ reqs = "" deflt = "" isdef = 0 key = "" for (i=3; i<=NF; ++i) if ($(i) == "requires" || $(i) == "default") { key = $(i) if (key == "default") isdef = 1 } else if (key == "requires") reqs = reqs " " $(i) else if (key == "default") deflt = deflt " " $(i) else break # Format error, handled below setting[$2] = reqs if (isdef && deflt == "") deflt = " " # as a flag to force output defaults[$2] = deflt next } # The order of the dependency lines (option, chunk, setting) is irrelevant # - the 'enables', 'requires' and 'if' settings will be used to determine # the correct order in the output and the final values in pnglibconf.h are # not order dependent. 'requires' and 'if' entries take precedence over # 'enables' from other options; if an option requires another option it # won't be set regardless of any options that enable it unless the other # option is also enabled. # # Similarly 'enables' trumps a NO_ definition in CFLAGS or pngusr.h # # For simplicity cycles in the definitions are regarded as errors, # even if they are not ambiguous. # A given NAME can be specified in as many 'option' lines as required, the # definitions are additive. # For backwards compatibility equivalent macros may be listed thus: # # = [NO_]NAME MACRO # Makes -DMACRO equivalent to -DPNG_NO_NAME or -DPNG_NAME_SUPPORTED # as appropriate. # # The definition is injected into the C compiler input when encountered # in the second pass (so all these definitions appear *after* the @ # lines!) # # 'NAME' is as above, but 'MACRO' is the full text of the equivalent # old, deprecated, macro. $1 == "=" && NF == 3{ print "#ifdef PNG_" $3 >out if ($2 ~ /^NO_/) print "# define PNG_" $2 >out else print "# define PNG_" $2 "_SUPPORTED" >out print "#endif" >out next } # Lines may be injected into the C compiler input by preceding them # with an "@" character. The line is copied with just the leading # @ removed. $1 ~ /^@/{ sub(/^[ ]*@/, "") print >out next } # Check for unreognized lines, because of the preprocessing chunk # format errors will be detected on the first pass independent of # any other format errors. { print "options.awk: bad line (" NR "):", $0 exit 1 } # For checking purposes names that start with "ok_" or "fail_" are # not output to pnglibconf.h and must be either enabled or disabled # respectively for the build to succeed. This allows interdependencies # between options of the form "at least one of" or "at most one of" # to be checked. For example: # # option FLOATING_POINT enables ok_math # option FIXED_POINT enables ok_math # This ensures that at least one of FLOATING_POINT and FIXED_POINT # must be set for the build to succeed. # # option fail_math requires FLOATING_POINT FIXED_POINT # This means the build will fail if *both* FLOATING_POINT and # FIXED_POINT are set (this is an example; in fact both are allowed.) # # If all these options were given the build would require exactly one # of the names to be enabled. END{ if (out == "/dev/null") { print "out=output.file must be given on the command line" exit 1 } if (pre) { if (pre != 1) { print "pre=0 or pre=1 must be given on command line" exit 1 } exit 0 } # Do the 'setting' values first, the algorithm the standard # tree walk (O(1)) done in an O(2) while/for loop; interations # settings x depth, outputing the deepest required macros # first. print "" >out print "/* SETTINGS */" >out print comment, "settings", cend >out finished = 0 while (!finished) { finished = 1 movement = 0 # done nothing for (i in setting) if (!doneset[i]) { nreqs = split(setting[i], r) if (nreqs > 0) { for (j=1; j<=nreqs; ++j) if (!doneset[r[j]]) break if (j<=nreqs) { finished = 0 continue # try a different setting } } # All the requirements have been processed, output # this setting. if (deb) print "setting", i print "" >out print "/* setting: ", i >out print " * requires:" setting[i] >out print " * default: ", defaults[i], "*/" >out if (defaults[i] == "") # no default, only check if defined print "#ifdef PNG_" i >out for (j=1; j<=nreqs; ++j) { print "# ifndef PNG_" r[j] >out print error, i, "requires", r[j] end >out print "# endif" >out } if (defaults[i] != "") # default handling print "#ifdef PNG_" i >out print def i, "PNG_" i end >out if (defaults[i] != "") { print "#else /*default*/" >out print def i defaults[i] end >out } print "#endif" >out doneset[i] = 1 ++movement } if (!finished && !movement) { print "setting: loop or missing setting in 'requires', cannot process:" for (i in setting) if (!doneset[i]) print " setting", i, "requires" setting[i] exit 1 } } print comment, "end of settings", cend >out # Now do the options - somewhat more complex. The dependency # tree is thus: # # name > name # name requires name # name if name # name enabledby name # # First build a list 'tree' by option of all the things on which # it depends. print "" >out print "/* OPTIONS */" >out print comment, "options", cend >out for (opt in enabledby) tree[opt] = 1 # may not be explicit options for (opt in option) tree[opt] = "" # so unlisted options marked for (opt in tree) { if (tree[opt] == 1) { tree[opt] = "" if (option[opt] != "") { print "internal error (1)" exit 1 } # Macros only listed in 'enables' remain off unless # one of the enabling macros is on. option[opt] = "off" } split("", list) # clear 'list' # Now add every requires, iffs or enabledby entry to 'list' # so that we can add a unique list of requirements to tree[i] split(requires[opt] iffs[opt] enabledby[opt], r) for (i in r) list[r[i]] = 1 for (i in list) tree[opt] = tree[opt] " " i } # print the tree for extreme debugging if (deb > 2) for (i in tree) if (i != "") print i, "depends-on" tree[i] # option[i] is now the complete list of all the tokens we may # need to output, go through it as above, depth first. finished = 0 while (!finished) { finished = 1 movement = 0 # done nothing for (i in option) if (!done[i]) { nreqs = split(tree[i], r) if (nreqs > 0) { for (j=1; j<=nreqs; ++j) if (!done[r[j]]) break if (j<=nreqs) { finished = 0 continue # next option } } # All the requirements have been processed, output # this option. An option is _SUPPORTED if: # # all 'requires' are _SUPPORTED AND # at least one of the 'if' options are _SUPPORTED AND # EITHER: # The name is _SUPPORTED (on the command line) # OR: # an 'enabledby' is _SUPPORTED # OR: # NO_name is not defined AND # the option is not marked "off" if (deb) print "option", i print "" >out print "/* option:", i, option[i] >out print " * requires: " requires[i] >out print " * if: " iffs[i] >out print " * enabled-by:" enabledby[i], "*/" >out print "#undef PNG_on" >out print "#define PNG_on 1" >out # requires nreqs = split(requires[i], r) for (j=1; j<=nreqs; ++j) { print "#ifndef PNG_" r[j] "_SUPPORTED" >out print "# undef PNG_on /*!" r[j] "*/" >out print "#endif" >out } # if nreqs = split(iffs[i], r) print "#undef PNG_no_if" >out if (nreqs > 0) { print "/* if" iffs[i], "*/" >out print "#define PNG_no_if 1" >out for (j=1; j<=nreqs; ++j) { print "#ifdef PNG_" r[j] "_SUPPORTED" >out print "# undef PNG_no_if /*" r[j] "*/" >out print "#endif" >out } print "#ifdef PNG_no_if /*missing if*/" >out print "# undef PNG_on" >out print "#endif" >out } print "#ifdef PNG_on /*requires, if*/" >out # enables print "# undef PNG_not_enabled" >out print "# define PNG_not_enabled 1" >out print " /* enabled by" enabledby[i], "*/" >out nreqs = split(enabledby[i], r) for (j=1; j<=nreqs; ++j) { print "#ifdef PNG_" r[j] "_SUPPORTED" >out print "# undef PNG_not_enabled /*" r[j] "*/" >out print "#endif" >out } print "# ifndef PNG_" i "_SUPPORTED /*!command line*/" >out print "# ifdef PNG_not_enabled /*!enabled*/" >out if (option[i] == "off") { print "# undef PNG_on /*default off*/" >out } else { print "# ifdef PNG_NO_" i >out print "# undef PNG_on /*turned off*/" >out print "# endif" >out print "# ifdef PNG_NO_" i "_SUPPORTED" >out print "# undef PNG_on /*turned off*/" >out print "# endif" >out } print "# endif /*!enabled*/" >out print "# ifdef PNG_on" >out # The _SUPPORTED macro must be defined so that dependent # options output later work. print "# define PNG_" i "_SUPPORTED" >out print "# endif" >out print "# endif /*!command line*/" >out # If PNG_on is still set the option should be defined in # pnglibconf.h print "# ifdef PNG_on" >out if (i ~ /^fail_/) print error, i, "if" iffs[i], "requires" requires[i] end >out else if (i !~ /^ok_/) print def i sup >out print "# endif /* definition */" >out print "#endif /*requires, if*/" >out if (logunsupported || i ~ /^ok_/) { print "#ifndef PNG_on" >out if (logunsupported) print und i une >out if (i ~ /^ok_/) print error, i, "if" iffs[i], "requires" requires[i] end >out print "#endif" >out } done[i] = 1 ++movement } if (!finished && !movement) { print "option: loop or missing option in dependency tree, cannot process:" for (i in option) if (!done[i]) { print " option", i, "depends on" tree[i], "needs:" nreqs = split(tree[i], r) if (nreqs > 0) for (j=1; j<=nreqs; ++j) if (!done[r[j]]) print " " r[j] } exit 1 } } print comment, "end of options", cend >out # Regular end - everything looks ok if (protect != "") print start "#endif", cx, protect, "*/" end >out }