options.awk 18.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
#!/bin/awk -f
# scripts/options.awk - library build configuration control
#
# libpng version 1.5.0beta17 - April 2, 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 <comment>
#   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 pnglconf.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 pnglconf.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
	    # pnglconf.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
}