# Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 1999, 2000, 2002, 2003
# Free Software Foundation, Inc.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 

# Please email any bugs, comments, and/or additions to this file to:
# bug-dejagnu@prep.ai.mit.edu

# This file was written by Rob Savoye. (rob@cygnus.com)

# this contains a list of gcc options and their respective directories.

#
# Find the pieces of libgloss for testing the GNU development tools
# needed to link a set of object files into an executable.
# This usually means setting the -L and -B paths correctly.
#
proc libgloss_link_flags { args } {
    global target_cpu
    global srcdir

    # libgloss doesn't work native
    if [isnative] {
	return ""
    }

    # if we're on a remote host, we can't search for the file, so we can only
    # use an installed compiler, so we don't add any paths here.
    if [is_remote host] {
	return ""
    }

    set gccpath "[get_multilibs]"

    # map the target_cpu to the proper libgloss directory. unfortunately, these
    # directory names are hardcoded into libgloss.
    switch -glob -- $target_cpu {
	"sparc86x" {
	    set cpu sparc
	}
	"sparclite" {
	    set cpu sparc
	}
	"sparclet" {
	    set cpu sparc
	}
	"sparc64*" {
	    set cpu sparc
	}
	"hppa*" {
	    set cpu pa
	}
	"mips*" {
	    set cpu mips
	}
	"powerpc*" {
	    set cpu rs6000
	}
	"d10v*" {
	    set cpu libnosys
	}
	"xscale*" {
	    set cpu arm
	}
	default {
	    set cpu $target_cpu
	}
    }

    set gloss_srcdir ""
    # look for the libgloss srcdir sp we can find the linker scripts
    set gloss_srcdir [lookfor_file ${srcdir} libgloss/$cpu]

    # set the proper paths for gcc if the target subdir exists, else assume we
    # have no libgloss support for this target.
    if { $gloss_srcdir == "" } {
	return ""
    }
    if [file exists $gccpath/libgloss/$cpu] {
	verbose "Libgloss path is $gccpath/libgloss/$cpu" 2
	return "-B$gccpath/libgloss/$cpu/ -L$gccpath/libgloss/$cpu -L$gloss_srcdir"
    } else {
	verbose -log "No libgloss support for this target." 2
	return ""
    }
}

# There aren't any, but we'll be orthogonal here.

proc libgloss_include_flags { args } {
    return ""
}

#
# Find the newlib libraries in the current source tree.
#
proc newlib_link_flags { args } {
    global tool_root_dir

    # libgloss doesn't work native
    if [isnative] {
	return ""
    }

    # if we're on a remote host, we can't search for the file, so we can only
    # use an installed compiler, so we don't add any paths here.
    if [is_remote host] {
	return ""
    }

    set ld_script_path [lookfor_file ${tool_root_dir} "ld/ldscripts"];
    if { $ld_script_path != "" } {
	set result "-L[file dirname $ld_script_path]"
    } else {
	set result ""
    }

    set gccpath "[get_multilibs]"

    verbose "Looking for $gccpath/newlib"
    if [file exists $gccpath/newlib] {
	verbose "Newlib path is $gccpath/newlib"
	return "$result -B$gccpath/newlib/ -L$gccpath/newlib"
    } else {
	verbose "No newlib support for this target"
	return "$result"
    }
}

proc newlib_include_flags { args } {
    global srcdir

    if [isnative] {
	return ""
    }

    if [is_remote host] {
	return ""
    }

    set gccpath "[get_multilibs]"

    if [file exists $gccpath/newlib] {
	verbose "Newlib path is $gccpath/newlib"

	set newlib_dir [lookfor_file ${srcdir} newlib/libc/include/assert.h]
	if { ${newlib_dir} != "" } {
	    set newlib_dir [file dirname ${newlib_dir}]
	}
	return " -I$gccpath/newlib/targ-include -I${newlib_dir}"
    } else {
	verbose "No newlib support for this target"
    }
}

proc libio_include_flags { args } {
    global srcdir
    global tool_root_dir

    if [is_remote host] {
	return ""
    }

    set gccpath "[get_multilibs]"

    if { $gccpath == "" } {
	set gccpath "$tool_root_dir";
    }

    set libio_bin_dir [lookfor_file ${gccpath} libio/_G_config.h];

    # linux doesn't build _G_config.h and the test above fails, so
    # we search for iostream.list too.
    if { $libio_bin_dir == "" } {
	set libio_bin_dir [lookfor_file ${gccpath} libio/iostream.list];
    }

    set libio_src_dir [lookfor_file ${srcdir} libio/Makefile.in]
    if { $libio_bin_dir != "" && $libio_src_dir != "" } {
	set libio_src_dir [file dirname ${libio_src_dir}]
	set libio_bin_dir [file dirname ${libio_bin_dir}];
	return " -I${libio_src_dir} -I${libio_bin_dir}"
    } else {
	return ""
    }
}

proc libio_link_flags { args } {
    if [is_remote host] {
	return ""
    }

    set gccpath "[get_multilibs]"

    set libio_dir [lookfor_file ${gccpath} libio/libio.a]
    if { $libio_dir != "" } {
	return "-L[file dirname ${libio_dir}]"
    } else {
	return ""
    }
}

proc g++_include_flags { args } {
    global srcdir
    global target_alias

    if [is_remote host] {
	return ""
    }

    set gccpath [get_multilibs]
    set libio_dir ""
    set flags ""

    set dir [lookfor_file ${srcdir} libg++]
    if { ${dir} != "" } {
	append flags " -I${dir} -I${dir}/src"
    }

    set dir [lookfor_file ${srcdir} libstdc++-v3]
    if { ${dir} != "" } {
	append flags " -I${dir}/include -I${dir}/include/std -I${dir}/include/c_std -I${dir}/libsupc++"
    }

    set dir [lookfor_file ${gccpath} libstdc++-v3]
    if { ${dir} != "" } {
	append flags " -I${dir}/include -I${dir}/include/${target_alias}"
    }

    set dir [lookfor_file ${srcdir} libstdc++]
    if { ${dir} != "" } {
	append flags " -I${dir} -I${dir}/stl"
    }

    return "$flags"
}

proc g++_link_flags { args } {
    global srcdir
    global ld_library_path

    set gccpath [get_multilibs];
    set libio_dir ""
    set flags ""
    set ld_library_path "."

    if { $gccpath != "" } {
	if [file exists "${gccpath}/lib/libstdc++.a"] {
	    append ld_library_path ":${gccpath}/lib"
	}
	if [file exists "${gccpath}/libg++/libg++.a"] {
	    append flags "-L${gccpath}/libg++ "
	    append ld_library_path ":${gccpath}/libg++"
	}
	if [file exists "${gccpath}/libstdc++/libstdc++.a"] {
	    append flags "-L${gccpath}/libstdc++ "
	    append ld_library_path ":${gccpath}/libstdc++"
	}
	if [file exists "${gccpath}/libstdc++-v3/src/.libs/libstdc++.a"] {
	    append flags "-L${gccpath}/libstdc++-v3/src/.libs "
	    append ld_library_path ":${gccpath}/libstdc++-v3/src/.libs"
	}
	if [file exists "${gccpath}/libiberty/libiberty.a"] {
	    append flags "-L${gccpath}/libiberty "
	}
	if [file exists "${gccpath}/librx/librx.a"] {
	    append flags "-L${gccpath}/librx "
	}
    } else {
	global tool_root_dir;

	set libgpp [lookfor_file ${tool_root_dir} libg++];
	if { $libgpp != "" } {
	    append flags "-L${libgpp} ";
	    append ld_library_path ":${libgpp}"
	}
	set libstdcpp [lookfor_file ${tool_root_dir} libstdc++];
	if { $libstdcpp != "" } {
	    append flags "-L${libstdcpp} ";
	    append ld_library_path ":${libstdcpp}"
	}
	set libiberty [lookfor_file ${tool_root_dir} libiberty];
	if { $libiberty != "" } {
	    append flags "-L${libiberty} ";
	}
	set librx [lookfor_file ${tool_root_dir} librx];
	if { $librx != "" } {
	    append flags "-L${librx} ";
	}
    }
    return "$flags"
}

proc libstdc++_include_flags { args } {
    global srcdir
    global target_alias

    if [is_remote host] {
	return ""
    }

    set gccpath [get_multilibs]
    set libio_dir ""
    set flags ""

    set dir [lookfor_file ${srcdir} libstdc++-v3]
    if { ${dir} != "" } {
	append flags " -I${dir}/include -I${dir}/include/std -I${dir}/include/c_std -I${dir}/libsupc++"
    }

    set gccpath [get_multilibs]

    set dir [lookfor_file ${gccpath} libstdc++-v3]
    if { ${dir} != "" } {
	append flags " -I${dir}/include -I${dir}/include/${target_alias}"
    }

    set dir [lookfor_file ${srcdir} libstdc++]
    if { ${dir} != "" } {
	append flags " -I${dir} -I${dir}/stl"
    }

    return "$flags"
}

proc libstdc++_link_flags { args } {
    global srcdir
    global ld_library_path

    set gccpath [get_multilibs];
    set libio_dir ""
    set flags ""

    if { $gccpath != "" } {
	if [file exists "${gccpath}/libstdc++/libstdc++.a"] {
	    append flags "-L${gccpath}/libstdc++ "
	    append ld_library_path ":${gccpath}/libstdc++"
	}
	if [file exists "${gccpath}/libiberty/libiberty.a"] {
	    append flags "-L${gccpath}/libiberty "
	}
	if [file exists "${gccpath}/librx/librx.a"] {
	    append flags "-L${gccpath}/librx "
	}
    } else {
	global tool_root_dir;

	set libstdcpp [lookfor_file ${tool_root_dir} libstdc++];
	if { $libstdcpp != "" } {
	    append flags "-L${libstdcpp} ";
	    append ld_library_path ":${libstdcpp}"
	}
	set libiberty [lookfor_file ${tool_root_dir} libiberty];
	if { $libiberty != "" } {
	    append flags "-L${libiberty} ";
	}
	set librx [lookfor_file ${tool_root_dir} librx];
	if { $librx != "" } {
	    append flags "-L${librx} ";
	}
    }
    return "$flags"
}

#
# Get the list of directories and -m options for gcc. This is kinda bogus that
# generic testing software needs support for gcc hardwired in, but to make 
# testing the GNU tools work right, there didn't seem to be any other way.
#

proc get_multilibs { args } {
    global target_alias
    global board
    global board_info

    # if we're on a remote host, we can't search for the file, so we can only
    # use an installed compiler, so we don't add any paths here.
    if [is_remote host] {
	return ""
    }

    if [info exists board] {
	set target_board $board;
    } else {
	set target_board [target_info name];
    }

    if { [llength $args] == 0 } {
	if [board_info $target_board exists multitop] {
	    return "[board_info $target_board multitop]";
	}

	set board_info($target_board,multitop) ""
    }

    if { [board_info $target_board exists compiler] } {
	set compiler [board_info $target_board compiler];
    } else {
	set compiler [find_gcc];
    }

    if { $compiler == "" } {
	return "";
    }

    foreach x "$compiler" {
	if [regexp "^-B" "$x"] {
	    regsub "^-B" "$x" "" comp_base_dir;
	    set comp_base_dir [file dirname $comp_base_dir];
	    break;
	}
    }

    regexp "/.* " $compiler compiler
    set compiler [string trimright $compiler " "]
    verbose "compiler is $compiler"

    if { [which $compiler] == 0 } {
	return "";
    }

    if { [board_info $target_board exists multilib_flags] } {
	set mopts [board_info $target_board multilib_flags];
    } else {
	set mopts ""
    }

    set default_multilib [exec $compiler --print-multi-lib]
    set default_multilib [lindex $default_multilib 0];
    set extra [string trimleft $default_multilib ".;@@"]

    # extract the options and their directory names as know by gcc
    foreach i "[exec $compiler --print-multi-lib]" {
    	if {$extra != ""} {
          # string trimright would do the wrong thing if we included
          # the leading @@ in $extra
 	  set i [string trimright $i $extra]
          set i [string trimright $i "@@"]
	}
	set opts ""
	set dir ""
	regexp -- "\[a-z0-9=/\.-\]*;" $i dir
	set dir [string trimright $dir "\;@"]
	regexp -- "\;@*\[\@a-zA-Z0-9=/\.-\]*" $i opts
	set opts [split [string trimleft $opts "\;@@"] "@@"]
	lappend multilibs "$dir {$opts }"

	# If args contains arguments don't use the first one as
	# multilib option unless it qualifies as a multilib option.
	if { [llength $args] > 0 } {
	    set override_opt [lindex $args 0]
	    foreach j $opts {
	        if {$j == $override_opt} {
	    	    set mopts $override_opt
	        }
	    }
	}
    }

    regsub "^-" $mopts "" moptions
    regsub -all " -" $moptions " " dirty_moptions
    set moptions ""
    foreach x [split $dirty_moptions " "] {
	if { $x != "" && [lsearch -exact $moptions $x] < 0 } {
	    lappend moptions $x
	}
    }

    if ![info exists comp_base_dir] {
	set comp_base_dir [file dirname [file dirname [file dirname [file dirname [file dirname [exec $compiler --print-prog-name=cc1]]]]]];
    }

    # search for the top level multilib directory
    set multitop [lookfor_file "${comp_base_dir}" "${target_alias}"]
    if { $multitop == "" } {
	set multitop [lookfor_file "${comp_base_dir}" "libraries"]
	if { $multitop == "" } {
	    set multitop "[lookfor_file ${comp_base_dir} gcc/xgcc]"
	    if { $multitop != "" } {
		set multitop [file dirname [file dirname $multitop]];
	    } else {
		return ""
	    }
	}
    }

    set gccpath [eval exec "$compiler" --print-multi-directory $mopts];
    set gccpath [lindex $gccpath 0];
    if { $gccpath != "" } {
	verbose "GCC path is $gccpath";
	if { [llength $args] == 0 } {
	    set board_info($target_board,multitop) "$multitop/$gccpath"
	}
	return "$multitop/$gccpath";
    }

    # extract the MULTILIB_MATCHES from dumpspecs
    set multimatches ""
    set lines [split [exec $compiler -dumpspecs] "\n"]  
    for {set i 0} {$i <= [llength $lines] - 1} {incr i 1} {
	if {"*multilib_matches:" == "[lindex $lines $i]"} {
	    set multimatches [lindex $lines [expr $i + 1]]
	    break
	}
    }
    # if we find some
    if {$multimatches != ""} {
	# Split it into a list of pairs. If an moptions are the first
	# of a pair, then replace it with the second.  If an moption
	# is not in multimatches, we assume it's not a multilib option

	set splitmatches [split $multimatches ";"]
	set multimatches ""
	foreach i $splitmatches {
	    lappend multimatches [split $i " "]
	}
	verbose "multimatches: $multimatches" 3

	verbose "options before multimatches: $moptions" 3
	set toptions $moptions
	set moptions ""
	foreach i $toptions {
	    foreach j $multimatches {
		verbose "comparing [lindex $j 0] == $i" 3
		if {[lindex $j 0] == $i} {
		    lappend moptions [lindex $j 1]
		}
	    }
	}
	verbose "options after multimatches: $moptions" 3
    }

    # make a list of -m<foo> options from the various compiler config variables
    set gccpath ""

    # compare the lists of gcc options with the list of support multilibs
    verbose "Supported multilibs are: $multilibs" 3
    set best 0;
    foreach i "$multilibs" {
	set hits 0
	set opts [lindex $i 1];
	if { [llength $opts] <= [llength $moptions] } {
	    foreach j "$moptions" {
		# see if all the -m<foo> options match any of the multilibs
		verbose "Looking in $i for $j" 3
		if { [lsearch -exact $opts $j] >= 0 } {
		    incr hits
		}
	    }

	    if { $hits > $best } {
		verbose "[lindex $i 0] is better, using as gcc path" 2
		set gccpath "[lindex $i 0]"
		set best $hits;
	    }
	}
    }
    if ![info exists multitop] {
	return "";
    }

    verbose "gccpath is $gccpath" 3

    if [file exists $multitop/$gccpath] {
	verbose "GCC path is $multitop/$gccpath" 3
	if { [llength $args] == 0 } {
	    set board_info($target_board,multitop) "$multitop/$gccpath"
	}
	return "$multitop/$gccpath"
    } else {
	verbose "GCC path is $multitop" 3
	if { [llength $args] == 0 } {
	    set board_info($target_board,multitop) "$multitop"
	}
	return "$multitop"
    }
}

proc find_binutils_prog { name } {
    global tool_root_dir;

    if ![is_remote host] {
    
	set file [lookfor_file $tool_root_dir $name];
	if { $file == "" } {
	    set file [lookfor_file $tool_root_dir ${name}-new];
	}
	if { $file == "" } {
	    set file [lookfor_file $tool_root_dir binutils/$name];
	}
	if { $file == "" } {
	    set file [lookfor_file $tool_root_dir binutils/${name}-new];
	}
	if { $file != "" } {
	    set NAME "$file";
	} else {
	    set NAME [transform $name];
	}
    } else {
	set NAME [transform $name]
    }
    return $NAME;
}

proc find_gcc {} {
    global tool_root_dir

    if ![is_remote host] {
	set file [lookfor_file $tool_root_dir xgcc];
	if { $file == "" } {
	    set file [lookfor_file $tool_root_dir gcc/xgcc];
	}
	if { $file != "" } {
	    set CC "$file -B[file dirname $file]/";
	} else {
	    set CC [transform gcc];
	}
    } else {
	set CC [transform gcc]
    }
    return $CC;
}

proc find_gcj {} {
    global tool_root_dir

    if ![is_remote host] {
	set file [lookfor_file $tool_root_dir gcj];
	if { $file == "" } {
	    set file [lookfor_file $tool_root_dir gcc/gcj];
	}
	if { $file != "" } {
	    set CC "$file -B[file dirname $file]/";
	} else {
	    set CC [transform gcj];
	}
    } else {
	set CC [transform gcj]
    }
    return $CC;
}

proc find_g++ {} {
    global tool_root_dir

    if ![is_remote host] {
	set file [lookfor_file $tool_root_dir g++];
	if { $file == "" } {
	    set file [lookfor_file $tool_root_dir gcc/g++];
	}
	if { $file != "" } {
	    set CC "$file -B[file dirname $file]/";
	} else {
	    set CC [transform g++];
	}
    } else {
	set CC [transform g++]
    }
    return $CC;
}

proc find_g77 {} {
    global tool_root_dir

    if ![is_remote host] {
	set file [lookfor_file $tool_root_dir g77];
	if { $file == "" } {
	    set file [lookfor_file $tool_root_dir gcc/g77];
	}
	if { $file != "" } {
	    set CC "$file -B[file dirname $file]/";
	} else {
	    set CC [transform g77];
	}
    } else {
	set CC [transform g77]
    }
    return $CC;
}

proc find_nm {} {
    global tool_root_dir

    set NM ""
    if ![is_remote host] {
	set NM [lookfor_file $tool_root_dir nm-new]
	if {$NM == ""} {
 	    set NM [lookfor_file $tool_root_dir binutils/nm-new]
	}
    }
    if { $NM == ""} {
	set NM [transform nm];
    }
    return $NM;
}

proc process_multilib_options { args } {
    global board;
    global board_variant_list;
    global is_gdb_remote;

    set is_gdb_remote 0;

    if [board_info $board exists multilib_flags] {
	return;
    }
    eval add_multilib_option $args;

    set multilib_flags "";

    foreach x $board_variant_list {
	regsub -all "^\[ \t\]*" "$x" "" x;
	regsub -all "\[ \t\]*$" "$x" "" x;

	if { $x == "" } {
	    continue;
	}
	case $x in {
	    { aout } {
		set_board_info obj_format "a.out";
	    }
	    { elf } {
		set_board_info obj_format "elf";
	    }
	    { pe } {
		set_board_info obj_format "pe";
	    }
	    { ecoff } {
		set_board_info obj_format "ecoff";
	    }
	    { stabs } {
		set_board_info debug_flags "-gstabs";
	    }
	    { dwarf2 } {
		set_board_info debug_flags "-gdwarf2";
	    }
	    { gdb:*=* } {
		regsub "^gdb:\[^=\]*=(.*)$" "$x" "\\1" value;
		regsub "^gdb:(\[^=\]*)=.*$" "$x" "\\1" variable;
		set_board_info $variable "$value";
	    }
	    { gdb*remote } {
		set is_gdb_remote 1;
	    }
	    { little*endian el EL } {
		append multilib_flags " -EL";
	    }
	    { big*endian eb EB } {
		append multilib_flags " -EB";
	    }
	    { "soft*float" } {
		append multilib_flags " -msoft-float"
	    }
	    { "-*" } {
		append multilib_flags " $x";
	    }
	    default {
		append multilib_flags " -m$x";
	    }
	}
    }
    set_board_info multilib_flags $multilib_flags;
}

proc add_multilib_option { args } {
    global board_variant_list

    if ![info exists board_variant_list] {
	set board_variant_list ""
    }
    set board_variant_list [concat $args $board_variant_list];
}

proc find_gas { } {
    global tool_root_dir

    set AS ""

    if ![is_remote host] {
	set AS [lookfor_file $tool_root_dir as-new];
	if { $AS == "" } {
	    set AS [lookfor_file $tool_root_dir gas/as-new];
	}
    }
    if { $AS == "" } {
	set AS [transform as];
    }
    return $AS;
}

proc find_ld { } {
    global tool_root_dir

    set LD ""

    if ![is_remote host] {
	set LD [lookfor_file $tool_root_dir ld-new];
	if { $LD == "" } {
	    set LD [lookfor_file $tool_root_dir ld/ld-new];
	}
    }
    if { $LD == "" } {
	set LD [transform ld];
    }
    return $LD;
}

proc build_wrapper { gluefile } {
    global libdir
    global tool

    if [target_info exists wrap_m68k_aout] {
	set flags "additional_flags=-DWRAP_M68K_AOUT";
	set result "";
    } elseif [target_info exists uses_underscores] {
	set flags "additional_flags=-DUNDERSCORES";
	set result "-Wl,-wrap,__exit -Wl,-wrap,_main -Wl,-wrap,_abort";
    } else {
	set flags "";
	if [target_info exists is_vxworks] {
	    set flags "additional_flags=-DVXWORKS";
	    set result "-Wl,-wrap,exit -Wl,-wrap,main -Wl,-wrap,abort";
	} else {
	    set result "-Wl,-wrap,exit -Wl,-wrap,_exit -Wl,-wrap,main -Wl,-wrap,abort";
	}
    }
    if [target_info exists wrap_compile_flags] {
	lappend flags "additional_flags=[target_info wrap_compile_flags]";
    }
    if { [target_compile ${libdir}/testglue.c ${gluefile} object $flags] == "" } {
      set gluefile [remote_download host ${gluefile} ${tool}_tg.o];
	return [list $gluefile $result];
    } else {
	return ""
    }
}


proc winsup_include_flags { args } {
    global srcdir

    if [isnative] {
	return ""
    }

    if [is_remote host] {
	return ""
    }

    set gccpath "[get_multilibs]"

    if [file exists $gccpath/winsup] {
	verbose "Winsup path is $gccpath/winsup"

	set winsup_dir [lookfor_file ${srcdir} winsup/include/windows.h]
	if { ${winsup_dir} != "" } {
	    set winsup_dir [file dirname ${winsup_dir}]
	    return " -I${winsup_dir}"
	}
    }
    verbose "No winsup support for this target"
    
}
#
# Find the winsup libraries in the current source tree.
#
proc winsup_link_flags { args } {
    # libgloss doesn't work native
    if [isnative] {
	return ""
    }

    # if we're on a remote host, we can't search for the file, so we can only
    # use an installed compiler, so we don't add any paths here.
    if [is_remote host] {
	return ""
    }

    set gccpath "[get_multilibs]"

    verbose "Looking for $gccpath/winsup"
    if [file exists $gccpath/winsup] {
	verbose "Winsup path is $gccpath/newlib"
	return "-B$gccpath/winsup/ -L$gccpath/winsup"
    } else {
	verbose "No winsup support for this target"
	return ""
    }
}
