diff options
author | Andreas K. Hüttel <dilfridge@gentoo.org> | 2017-10-18 00:44:49 +0200 |
---|---|---|
committer | Sergei Trofimovich <slyfox@gentoo.org> | 2019-02-16 10:10:44 +0000 |
commit | 9a17ad629fcccb62d2d24d9c9a7a4c1abcda7020 (patch) | |
tree | 1f5d24174d96d97bacfa55e65ab6e342fcee6af1 | |
parent | Gentoo: opcodes: link against libbfd.la for rpath deps (diff) | |
download | binutils-gdb-9a17ad629fcccb62d2d24d9c9a7a4c1abcda7020.tar.gz binutils-gdb-9a17ad629fcccb62d2d24d9c9a7a4c1abcda7020.tar.bz2 binutils-gdb-9a17ad629fcccb62d2d24d9c9a7a4c1abcda7020.zip |
Gentoo: Pass --hash-style=sysv to ld in its testsuite
-rw-r--r-- | ld/testsuite/lib/ld-lib.exp | 769 |
1 files changed, 767 insertions, 2 deletions
diff --git a/ld/testsuite/lib/ld-lib.exp b/ld/testsuite/lib/ld-lib.exp index dae6673758d..34299cca93d 100644 --- a/ld/testsuite/lib/ld-lib.exp +++ b/ld/testsuite/lib/ld-lib.exp @@ -165,7 +165,7 @@ proc default_ld_relocate { ld target objects } { global HOSTING_EMU remote_file host delete $target - return [run_host_cmd_yesno "$ld" "$HOSTING_EMU -o $target -r $objects"] + return [run_host_cmd_yesno "$ld" "$HOSTING_EMU --hash-style=sysv -o $target -r $objects"] } # Check to see if ld is being invoked with a non-endian output format @@ -196,8 +196,20 @@ proc default_ld_link { ld target objects } { set flags [big_or_little_endian] } + # Hilariously, this procedure is called sometimes with $CC and sometimes + # with $ld as parameter. If we want to change the default behaviour, we + # need to take the different option formats into account. + # We check the $ld parameter for + # - .*ld/ld-new -> we're working with the new linker + # - otherwise -> we're likely working with the system compiler + if {[regexp {ld/ld-new$} $ld]} { + set gentoosysv "--hash-style=sysv" + } else { + set gentoosysv "-Wl,--hash-style=sysv" + } + remote_file host delete $target - set exec_output [run_host_cmd "$ld" "$flags -o $target $objects"] + set exec_output [run_host_cmd "$ld" "$gentoosysv $flags -o $target $objects"] set exec_output [prune_warnings $exec_output] # We don't care if we get a warning about a non-existent start @@ -409,6 +421,759 @@ proc ld_link_defsyms {} { return $flags } +# run_dump_test FILE (optional:) EXTRA_OPTIONS +# Copied from gas testsuite, tweaked and further extended. +# +# Assemble a .s file, then run some utility on it and check the output. +# +# There should be an assembly language file named FILE.s in the test +# suite directory, and a pattern file called FILE.d. `run_dump_test' +# will assemble FILE.s, run some tool like `objdump', `objcopy', or +# `nm' on the .o file to produce textual output, and then analyze that +# with regexps. The FILE.d file specifies what program to run, and +# what to expect in its output. +# +# The FILE.d file begins with zero or more option lines, which specify +# flags to pass to the assembler, the program to run to dump the +# assembler's output, and the options it wants. The option lines have +# the syntax: +# +# # OPTION: VALUE +# +# OPTION is the name of some option, like "name" or "objdump", and +# VALUE is OPTION's value. The valid options are described below. +# Whitespace is ignored everywhere, except within VALUE. The option +# list ends with the first line that doesn't match the above syntax +# (hmm, not great for error detection). +# +# The optional EXTRA_OPTIONS argument to `run_dump_test' is a list of +# two-element lists. The first element of each is an option name, and +# the second additional arguments to be added on to the end of the +# option list as given in FILE.d. (If omitted, no additional options +# are added.) +# +# The interesting options are: +# +# name: TEST-NAME +# The name of this test, passed to DejaGNU's `pass' and `fail' +# commands. If omitted, this defaults to FILE, the root of the +# .s and .d files' names. +# +# as: FLAGS +# When assembling, pass FLAGS to the assembler. +# If assembling several files, you can pass different assembler +# options in the "source" directives. See below. +# +# ld: FLAGS +# Link assembled files using FLAGS, in the order of the "source" +# directives, when using multiple files. +# +# ld_after_inputfiles: FLAGS +# Similar to "ld", but put after all input files. +# +# objcopy_objects: FLAGS +# Run objcopy with the specified flags after assembling any source +# that has the special marker RUN_OBJCOPY in the source specific +# flags. +# +# objcopy_linked_file: FLAGS +# Run objcopy on the linked file with the specified flags. +# This lets you transform the linked file using objcopy, before the +# result is analyzed by an analyzer program specified below (which +# may in turn *also* be objcopy). +# +# PROG: PROGRAM-NAME +# The name of the program to run to analyze the .o file produced +# by the assembler or the linker output. This can be omitted; +# run_dump_test will guess which program to run by seeing which of +# the flags options below is present. +# +# readelf: FLAGS +# objdump: FLAGS +# nm: FLAGS +# objcopy: FLAGS +# Use the specified program to analyze the assembler or linker +# output file, and pass it FLAGS, in addition to the output name. +# Note that they are run with LC_ALL=C in the environment to give +# consistent sorting of symbols. +# +# source: SOURCE [FLAGS] +# Assemble the file SOURCE.s using the flags in the "as" directive +# and the (optional) FLAGS. If omitted, the source defaults to +# FILE.s. +# This is useful if several .d files want to share a .s file. +# More than one "source" directive can be given, which is useful +# when testing linking. +# +# dump: DUMP +# Match against DUMP.d. If omitted, this defaults to FILE.d. This +# is useful if several .d files differ by options only. Options are +# always read from FILE.d. +# +# xfail: TARGET +# The test is expected to fail on TARGET. This may occur more than +# once. +# +# target: TARGET +# Only run the test for TARGET. +# You may provide target name "cfi" for any target supporting the +# CFI statements. You may provide target name "shared" for any +# target supporting shared libraries. Otherwise TARGET is called +# as a TCL procedure if surrounded by square brackets, or passed +# to "istarget" if not. +# This may occur more than once; the target being tested must match +# at least one. Otherwise the test will be marked unsupported. +# +# alltargets: TARGET +# Only run the test for TARGET. +# The syntax for TARGET is as with 'target'. +# This may occur more than once; the target being tested must match +# all of them. Otherwise the test will be marked unsupported. +# +# notarget: TARGET +# Do not run the test for TARGET. +# The syntax for TARGET is as with 'target'. +# This may occur more than once; the target being tested must not +# match any of them. Otherwise the test will be marked unsupported. +# +# skip: TARGET +# anyskip: TARGET +# noskip: TARGET +# These are exactly the same as "notarget", "alltargets" and +# "target" respectively, except that they do nothing at all if the +# check fails. They should only be used in groups, to construct a +# single test which is run on all targets but with variant options +# or expected output on some targets. (For example, see +# gas/arm/inst.d and gas/arm/wince_inst.d.) +# +# error: REGEX +# An error with message matching REGEX must be emitted for the test +# to pass. The PROG, readelf, objdump, nm and objcopy options have +# no meaning and need not be supplied if this is present. Multiple +# "error" directives append to the expected linker error message. +# +# error_output: FILE +# Means the same as 'error', except the regular expression lines +# are contains in FILE. +# +# warning: REGEX +# Expect a linker warning matching REGEX. It is an error to issue +# both "error" and "warning". Multiple "warning" directives +# append to the expected linker warning message. +# +# warning_output: FILE +# Means the same as 'warning', except the regular expression +# lines are contains in FILE. +# +# map: FILE +# Adding this option will cause the linker to generate a linker +# map file, using the -Map=MAPFILE command line option. If +# there is no -Map=MAPFILE in the 'ld: FLAGS' then one will be +# added to the linker command line. The contents of the +# generated MAPFILE are then compared against the regexp lines +# in FILE using `regexp_diff' (see below for details). +# +# Each option may occur at most once unless otherwise mentioned. +# +# After the option lines come regexp lines. `run_dump_test' calls +# `regexp_diff' to compare the output of the dumping tool against the +# regexps in FILE.d. `regexp_diff' is defined in binutils-common.exp; +# see further comments there. +# +proc run_dump_test { name {extra_options {}} } { + global subdir srcdir + global OBJDUMP NM AS OBJCOPY READELF LD + global OBJDUMPFLAGS NMFLAGS ASFLAGS OBJCOPYFLAGS READELFFLAGS LDFLAGS + global host_triplet runtests + global env verbose + global ld_elf_shared_opt + + if { [is_elf_format] && [check_shared_lib_support] } { + set ld_extra_opt "$ld_elf_shared_opt" + } else { + set ld_extra_opt "" + } + + if [string match "*/*" $name] { + set file $name + set name [file tail $name] + } else { + set file "$srcdir/$subdir/$name" + } + + if ![runtest_file_p $runtests $name] then { + return + } + + set opt_array [slurp_options "${file}.d"] + if { $opt_array == -1 } { + perror "error reading options from $file.d" + unresolved $subdir/$name + return + } + set dumpfile tmpdir/dump.out + set run_ld 0 + set run_objcopy 0 + set objfile_names {} + set opts(as) {} + set opts(ld) {} + set opts(ld_after_inputfiles) {} + set opts(xfail) {} + set opts(target) {} + set opts(alltargets) {} + set opts(notarget) {} + set opts(skip) {} + set opts(anyskip) {} + set opts(noskip) {} + set opts(objdump) {} + set opts(nm) {} + set opts(objcopy) {} + set opts(readelf) {} + set opts(name) {} + set opts(PROG) {} + set opts(source) {} + set opts(dump) {} + set opts(error) {} + set opts(warning) {} + set opts(error_output) {} + set opts(warning_output) {} + set opts(objcopy_linked_file) {} + set opts(objcopy_objects) {} + set opts(map) {} + + foreach i $opt_array { + set opt_name [lindex $i 0] + set opt_val [lindex $i 1] + if ![info exists opts($opt_name)] { + perror "unknown option $opt_name in file $file.d" + unresolved $subdir/$name + return + } + + switch -- $opt_name { + xfail {} + target {} + alltargets {} + notarget {} + skip {} + anyskip {} + noskip {} + warning {} + error {} + source { + # Move any source-specific as-flags to a separate list to + # simplify processing. + if { [llength $opt_val] > 1 } { + lappend asflags [lrange $opt_val 1 end] + set opt_val [lindex $opt_val 0] + } else { + lappend asflags {} + } + + # Create the object file name based on nothing but the source + # file name. + set new_objfile \ + [concat tmpdir/[file rootname [file tail [lindex $opt_val 0]]].o] + # But, sometimes, we have the exact same source filename in + # different directories (foo/src.s bar/src.s) which would lead + # us to try and create two src.o files. We detect this + # conflict here, and instead create src.o and src1.o. + set j 0 + while { [lsearch $objfile_names $new_objfile] != -1 } { + incr j + set new_objfile \ + [concat tmpdir/[file rootname [file tail [lindex $opt_val 0]]]${j}.o] + } + lappend objfile_names $new_objfile + } + default { + if [string length $opts($opt_name)] { + perror "option $opt_name multiply set in $file.d" + unresolved $subdir/$name + return + } + + # A single "# ld:" with no options should do the right thing. + if { $opt_name == "ld" } { + set run_ld 1 + } + # Likewise objcopy_linked_file. + if { $opt_name == "objcopy_linked_file" } { + set run_objcopy 1 + } + } + } + if { $opt_name == "as" || $opt_name == "ld" } { + set opt_val [subst $opt_val] + } + + # Append differently whether it's a message (without space) or + # an option or list (with space). + switch -- $opt_name { + warning - + error { + append opts($opt_name) $opt_val + } + default { + set opts($opt_name) [concat $opts($opt_name) $opt_val] + } + } + } + + foreach i $extra_options { + set opt_name [lindex $i 0] + set opt_val [lindex $i 1] + if ![info exists opts($opt_name)] { + perror "unknown option $opt_name given in extra_opts" + unresolved $subdir/$name + return + } + # Add extra option to end of existing option, adding space + # if necessary. + if { ![regexp "warning|error" $opt_name] + && [string length $opts($opt_name)] } { + append opts($opt_name) " " + } + append opts($opt_name) $opt_val + } + + foreach opt { as ld } { + regsub {\[big_or_little_endian\]} $opts($opt) \ + [big_or_little_endian] opts($opt) + } + + if { $opts(name) == "" } { + set testname "$subdir/$name" + } else { + set testname $opts(name) + } + + # Decide early whether we should run the test for this target. + if { [llength $opts(noskip)] > 0 } { + set targmatch 0 + foreach targ $opts(noskip) { + if [match_target $targ] { + set targmatch 1 + break + } + } + if { $targmatch == 0 } { + return + } + } + foreach targ $opts(anyskip) { + if ![match_target $targ] { + return + } + } + foreach targ $opts(skip) { + if [match_target $targ] { + return + } + } + if { [llength $opts(target)] > 0 } { + set targmatch 0 + foreach targ $opts(target) { + if [match_target $targ] { + set targmatch 1 + break + } + } + if { $targmatch == 0 } { + unsupported $testname + return + } + } + foreach targ $opts(alltargets) { + if ![match_target $targ] { + unsupported $testname + return + } + } + foreach targ $opts(notarget) { + if [match_target $targ] { + unsupported $testname + return + } + } + + set program "" + # It's meaningless to require an output-testing method when we + # expect an error. + if { $opts(error) == "" && $opts(error_output) == "" } { + if {$opts(PROG) != ""} { + switch -- $opts(PROG) { + objdump { set program objdump } + nm { set program nm } + objcopy { set program objcopy } + readelf { set program readelf } + default + { perror "unrecognized program option $opts(PROG) in $file.d" + unresolved $testname + return } + } + } else { + # Guess which program to run, by seeing which option was specified. + foreach p {objdump objcopy nm readelf} { + if {$opts($p) != ""} { + if {$program != ""} { + perror "ambiguous dump program in $file.d" + unresolved $testname + return + } else { + set program $p + } + } + } + } + if { $program == "" \ + && $opts(map) == "" \ + && $opts(warning) == "" \ + && $opts(warning_output) == "" \ + && $opts(error) == "" \ + && $opts(error_output) == "" } { + perror "dump program unspecified in $file.d" + unresolved $testname + return + } + } + + if { $opts(source) == "" } { + set sourcefiles [list ${file}.s] + set asflags [list ""] + set objfile_names [list tmpdir/[file tail ${file}].o] + } else { + set sourcefiles {} + foreach sf $opts(source) { + if { [string match "/*" $sf] } { + lappend sourcefiles "$sf" + } else { + lappend sourcefiles "$srcdir/$subdir/$sf" + } + } + } + + if { $opts(dump) == "" } { + set dfile ${file}.d + } else { + set dfile $srcdir/$subdir/$opts(dump) + } + + # Time to setup xfailures. + foreach targ $opts(xfail) { + setup_xfail $targ + } + + # Assemble each file. + set objfiles {} + for { set i 0 } { $i < [llength $sourcefiles] } { incr i } { + set sourcefile [lindex $sourcefiles $i] + set sourceasflags [lindex $asflags $i] + set run_objcopy_objects 0 + + if { [string match "*RUN_OBJCOPY*" $sourceasflags] } { + set run_objcopy_objects 1 + } + regsub "RUN_OBJCOPY" $sourceasflags "" sourceasflags + + set objfile [lindex $objfile_names $i] + catch "exec rm -f $objfile" exec_output + lappend objfiles $objfile + set cmd "$AS $ASFLAGS $opts(as) $sourceasflags -o $objfile $sourcefile" + + send_log "$cmd\n" + set cmdret [remote_exec host [concat sh -c [list "$cmd 2>&1"]] "" "/dev/null" "ld.tmp"] + remote_upload host "ld.tmp" + set comp_output [prune_warnings [file_contents "ld.tmp"]] + remote_file host delete "ld.tmp" + remote_file build delete "ld.tmp" + + if { [lindex $cmdret 0] != 0 || ![string match "" $comp_output] } then { + send_log -- "$comp_output\n" + verbose "$comp_output" 3 + + set exitstat "succeeded" + if { $cmdret != 0 } { set exitstat "failed" } + verbose -log "$exitstat with: <$comp_output>" + fail $testname + return + } + + if { $run_objcopy_objects } { + set cmd "$OBJCOPY $opts(objcopy_objects) $objfile" + + send_log "$cmd\n" + set cmdret [remote_exec host [concat sh -c [list "$cmd 2>&1"]] \ + "" "/dev/null" "objcopy.tmp"] + remote_upload host "objcopy.tmp" + set comp_output [prune_warnings [file_contents "objcopy.tmp"]] + remote_file host delete "objcopy.tmp" + remote_file build delete "objcopy.tmp" + + if { [lindex $cmdret 0] != 0 \ + || ![string match "" $comp_output] } { + send_log -- "$comp_output\n" + verbose "$comp_output" 3 + + set exitstat "succeeded" + if { $cmdret != 0 } { set exitstat "failed" } + verbose -log "$exitstat with: <$comp_output>" + fail $testname + return + } + } + } + + if { (($opts(warning) != "") && ($opts(error) != "")) \ + || (($opts(warning) != "") && ($opts(error_output) != "")) \ + || (($opts(warning) != "") && ($opts(warning_output) != "")) \ + || (($opts(error) != "") && ($opts(warning_output) != "")) \ + || (($opts(error) != "") && ($opts(error_output) != "")) \ + || (($opts(warning_output) != "") && ($opts(error_output) != "")) } { + perror "$testname: bad mix of warning, error, warning_output, and error_output test-directives" + unresolved $testname + return + } + + set check_ld(source) "" + set check_ld(terminal) 0 + if { $opts(error) != "" \ + || $opts(warning) != "" \ + || $opts(error_output) != "" \ + || $opts(warning_output) != "" } { + + if { $opts(error) != "" || $opts(error_output) != "" } { + set check_ld(terminal) 1 + } else { + set check_ld(terminal) 0 + } + + if { $opts(error) != "" || $opts(warning) != "" } { + set check_ld(source) "regex" + if { $opts(error) != "" } { + set check_ld(regex) $opts(error) + } else { + set check_ld(regex) $opts(warning) + } + } else { + set check_ld(source) "file" + if { $opts(error_output) != "" } { + set check_ld(file) $opts(error_output) + } else { + set check_ld(file) $opts(warning_output) + } + } + } + + # Perhaps link the file(s). + if { $run_ld } { + set objfile "tmpdir/dump" + catch "exec rm -f $objfile" exec_output + + # Add -L$srcdir/$subdir so that the linker command can use + # linker scripts in the source directory. + set cmd "$LD --hash-style=sysv $ld_extra_opt $LDFLAGS -L$srcdir/$subdir \ + $opts(ld) -o $objfile $objfiles $opts(ld_after_inputfiles)" + + # If needed then check for, or add a -Map option. + set mapfile "" + if { $opts(map) != "" } then { + if { [regexp -- "-Map=(\[^ \]+)" $cmd all mapfile] } then { + # Found existing mapfile option + verbose -log "Existing mapfile '$mapfile' found" + } else { + # No mapfile option. + set mapfile "tmpdir/dump.map" + verbose -log "Adding mapfile '$mapfile'" + set cmd "$cmd -Map=$mapfile" + } + } + + send_log "$cmd\n" + set cmdret [remote_exec host [concat sh -c [list "$cmd 2>&1"]] "" "/dev/null" "ld.tmp"] + remote_upload host "ld.tmp" + set comp_output [file_contents "ld.tmp"] + remote_file host delete "ld.tmp" + remote_file build delete "ld.tmp" + set cmdret [lindex $cmdret 0] + + if { $cmdret == 0 && $run_objcopy } { + set infile $objfile + set objfile "tmpdir/dump1" + remote_file host delete $objfile + + # Note that we don't use OBJCOPYFLAGS here; any flags must be + # explicitly specified. + set cmd "$OBJCOPY $opts(objcopy_linked_file) $infile $objfile" + + send_log "$cmd\n" + set cmdret [remote_exec host [concat sh -c [list "$cmd 2>&1"]] "" "/dev/null" "ld.tmp"] + remote_upload host "ld.tmp" + append comp_output [file_contents "ld.tmp"] + remote_file host delete "ld.tmp" + remote_file build delete "ld.tmp" + set cmdret [lindex $cmdret 0] + } + + regsub "\n$" $comp_output "" comp_output + if { $cmdret != 0 || $comp_output != "" || $check_ld(source) != "" } then { + set exitstat "succeeded" + if { $cmdret != 0 } { set exitstat "failed" } + + if { $check_ld(source) == "regex" } { + verbose -log "$exitstat with: <$comp_output>, expected: <$check_ld(regex)>" + } elseif { $check_ld(source) == "file" } { + verbose -log "$exitstat with: <$comp_output>, expected in file $check_ld(file)" + set_file_contents "tmpdir/ld.messages" "$comp_output" + } else { + verbose -log "$exitstat with: <$comp_output>, no expected output" + } + send_log -- "$comp_output\n" + verbose "$comp_output" 3 + + if { (($check_ld(source) == "") == ($comp_output == "")) \ + && (($cmdret == 0) == ($check_ld(terminal) == 0)) \ + && ((($check_ld(source) == "regex") \ + && ($check_ld(regex) == "") == ($comp_output == "") \ + && [regexp -- $check_ld(regex) $comp_output]) \ + || (($check_ld(source) == "file") \ + && (![regexp_diff "tmpdir/ld.messages" "$srcdir/$subdir/$check_ld(file)"]))) } { + # We have the expected output from ld. + if { $check_ld(terminal) || $program == "" } { + pass $testname + return + } + } else { + fail $testname + return + } + } + + if { $opts(map) != "" } then { + # Check the map file matches. + set map_pattern_file $srcdir/$subdir/$opts(map) + verbose -log "Compare '$mapfile' against '$map_pattern_file'" + if { [regexp_diff $mapfile $map_pattern_file] } then { + fail "$testname (map file check)" + } else { + pass "$testname (map file check)" + } + + if { $program == "" } then { + return + } + } + } else { + set objfile [lindex $objfiles 0] + } + + # We must not have expected failure if we get here. + if { $opts(error) != "" } { + fail $testname + return + } + + set progopts1 $opts($program) + eval set progopts \$[string toupper $program]FLAGS + eval set binary \$[string toupper $program] + + if { ![is_remote host] && [which $binary] == 0 } { + untested $testname + return + } + + if { $progopts1 == "" } { set $progopts1 "-r" } + verbose "running $binary $progopts $progopts1" 3 + + # Objcopy, unlike the other two, won't send its output to stdout, + # so we have to run it specially. + set cmd "$binary $progopts $progopts1 $objfile > $dumpfile" + if { $program == "objcopy" } { + set cmd "$binary $progopts $progopts1 $objfile $dumpfile" + } + + # Ensure consistent sorting of symbols + if {[info exists env(LC_ALL)]} { + set old_lc_all $env(LC_ALL) + } + set env(LC_ALL) "C" + send_log "$cmd\n" + set cmdret [remote_exec host [concat sh -c [list "$cmd 2>ld.tmp"]] "" "/dev/null"] + set cmdret [lindex $cmdret 0] + remote_upload host "ld.tmp" + set comp_output [prune_warnings [file_contents "ld.tmp"]] + remote_file host delete "ld.tmp" + remote_file build delete "ld.tmp" + if {[info exists old_lc_all]} { + set env(LC_ALL) $old_lc_all + } else { + unset env(LC_ALL) + } + if { $cmdret != 0 || $comp_output != "" } { + send_log "exited abnormally with $cmdret, output:$comp_output\n" + fail $testname + return + } + + if { $verbose > 2 } then { verbose "output is [file_contents $dumpfile]" 3 } + if { [regexp_diff $dumpfile "${dfile}"] } then { + fail $testname + if { $verbose == 2 } then { verbose "output is [file_contents $dumpfile]" 2 } + return + } + + pass $testname +} + +proc slurp_options { file } { + # If options_regsub(foo) is set to {a b}, then the contents of a + # "#foo:" line will have regsub -all applied to replace a with b. + global options_regsub + + if [catch { set f [open $file r] } x] { + #perror "couldn't open `$file': $x" + perror "$x" + return -1 + } + set opt_array {} + # whitespace expression + set ws {[ ]*} + set nws {[^ ]*} + # whitespace is ignored anywhere except within the options list; + # option names are alphabetic plus underscore only. + set pat "^#${ws}(\[a-zA-Z_\]*)$ws:${ws}(.*)$ws\$" + while { [gets $f line] != -1 } { + set line [string trim $line] + # Whitespace here is space-tab. + if [regexp $pat $line xxx opt_name opt_val] { + # match! + if [info exists options_regsub($opt_name)] { + set subst $options_regsub($opt_name) + regsub -all -- [lindex $subst 0] $opt_val [lindex $subst 1] \ + opt_val + } + lappend opt_array [list $opt_name $opt_val] + } else { + break + } + } + close $f + return $opt_array +} + +proc file_contents { filename } { + set file [open $filename r] + set contents [read $file] + close $file + return $contents +} + +proc set_file_contents { filename contents } { + set file [open $filename w] + puts $file "$contents" + close $file +} + # Create an archive using ar # proc ar_simple_create { ar aropts target objects } { |