diff options
Diffstat (limited to 'tools/mkdepend.tcl')
-rw-r--r-- | tools/mkdepend.tcl | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/tools/mkdepend.tcl b/tools/mkdepend.tcl new file mode 100644 index 0000000..de5fdba --- /dev/null +++ b/tools/mkdepend.tcl @@ -0,0 +1,420 @@ +#============================================================================== +# +# mkdepend : generate dependency information from C/C++ files +# +# Copyright (c) 1998, Nat Pryce +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and its documentation for any purpose, provided that the +# above copyright notice and the following two paragraphs appear in +# all copies of this software. +# +# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +# THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHOR HAS BEEN ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" +# BASIS, AND THE AUTHOR HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, +# UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +#============================================================================== +# +# Modified heavily by David Gravereaux <davygrvy@pobox.com> about 9/17/2006. +# Original can be found @ +# http://web.archive.org/web/20070616205924/http://www.doc.ic.ac.uk/~np2/software/mkdepend.html +#============================================================================== + +array set mode_data {} +set mode_data(vc32) {cl -nologo -E} + +set source_extensions [list .c .cpp .cxx .cc] + +set excludes [list] +if [info exists env(INCLUDE)] { + set rawExcludes [split [string trim $env(INCLUDE) ";"] ";"] + foreach exclude $rawExcludes { + lappend excludes [file normalize $exclude] + } +} + + +# openOutput -- +# +# Opens the output file. +# +# Arguments: +# file The file to open +# +# Results: +# None. + +proc openOutput {file} { + global output + set output [open $file w] + puts $output "# Automatically generated at [clock format [clock seconds] -format "%Y-%m-%dT%H:%M:%S"] by [info script]\n" +} + +# closeOutput -- +# +# Closes output file. +# +# Arguments: +# none +# +# Results: +# None. + +proc closeOutput {} { + global output + if {[string match stdout $output] != 0} { + close $output + } +} + +# readDepends -- +# +# Read off CCP pipe for #line references. +# +# Arguments: +# chan The pipe channel we are reading in. +# +# Results: +# Raw dependency list pairs. + +proc readDepends {chan} { + set line "" + array set depends {} + + while {[gets $chan line] != -1} { + if {[regexp {^#line [0-9]+ \"(.*)\"$} $line dummy fname] != 0} { + set fname [file normalize $fname] + if {![info exists target]} { + # this is ourself + set target $fname + puts stderr "processing [file tail $fname]" + } else { + # don't include ourselves as a dependency of ourself. + if {![string compare $fname $target]} {continue} + # store in an array so multiple occurances are not counted. + set depends($target|$fname) "" + } + } + } + + set result {} + foreach n [array names depends] { + set pair [split $n "|"] + lappend result [list [lindex $pair 0] [lindex $pair 1]] + } + + return $result +} + +# writeDepends -- +# +# Write the processed list out to the file. +# +# Arguments: +# out The channel to write to. +# depends The list of dependency pairs +# +# Results: +# None. + +proc writeDepends {out depends} { + foreach pair $depends { + puts $out "[lindex $pair 0] : \\\n\t[join [lindex $pair 1] " \\\n\t"]" + } +} + +# stringStartsWith -- +# +# Compares second string to the beginning of the first. +# +# Arguments: +# str The string to test the beginning of. +# prefix The string to test against +# +# Results: +# the result of the comparison. + +proc stringStartsWith {str prefix} { + set front [string range $str 0 [expr {[string length $prefix] - 1}]] + return [expr {[string compare [string tolower $prefix] \ + [string tolower $front]] == 0}] +} + +# filterExcludes -- +# +# Remove non-project header files. +# +# Arguments: +# depends List of dependency pairs. +# excludes List of directories that should be removed +# +# Results: +# the processed dependency list. + +proc filterExcludes {depends excludes} { + set filtered {} + + foreach pair $depends { + set excluded 0 + set file [lindex $pair 1] + + foreach dir $excludes { + if [stringStartsWith $file $dir] { + set excluded 1 + break; + } + } + + if {!$excluded} { + lappend filtered $pair + } + } + + return $filtered +} + +# replacePrefix -- +# +# Take the normalized search path and put back the +# macro name for it. +# +# Arguments: +# file filename. +# +# Results: +# filename properly replaced with macro for it. + +proc replacePrefix {file} { + global srcPathList srcPathReplaceList + + foreach was $srcPathList is $srcPathReplaceList { + regsub $was $file $is file + } + return $file +} + +# rebaseFiles -- +# +# Replaces normalized paths with original macro names. +# +# Arguments: +# depends Dependency pair list. +# +# Results: +# The processed dependency pair list. + +proc rebaseFiles {depends} { + set rebased {} + foreach pair $depends { + lappend rebased [list \ + [replacePrefix [lindex $pair 0]] \ + [replacePrefix [lindex $pair 1]]] + + } + return $rebased +} + +# compressDeps -- +# +# Compresses same named tragets into one pair with +# multiple deps. +# +# Arguments: +# depends Dependency pair list. +# +# Results: +# The processed list. + +proc compressDeps {depends} { + array set compressed [list] + + foreach pair $depends { + lappend compressed([lindex $pair 0]) [lindex $pair 1] + } + + set result [list] + foreach n [array names compressed] { + lappend result [list $n [lsort $compressed($n)]] + } + + return $result +} + +# addSearchPath -- +# +# Adds a new set of path and replacement string to the global list. +# +# Arguments: +# newPathInfo comma seperated path and replacement string +# +# Results: +# None. + +proc addSearchPath {newPathInfo} { + global srcPathList srcPathReplaceList + + set infoList [split $newPathInfo ,] + lappend srcPathList [file normalize [lindex $infoList 0]] + lappend srcPathReplaceList [lindex $infoList 1] +} + + +# displayUsage -- +# +# Displays usage to stderr +# +# Arguments: +# none. +# +# Results: +# None. + +proc displayUsage {} { + puts stderr "mkdepend.tcl \[options\] genericDir,macroName compatDir,macroName platformDir,macroName" +} + +# readInputListFile -- +# +# Open and read the object file list. +# +# Arguments: +# objectListFile - name of the file to open. +# +# Results: +# None. + +proc readInputListFile {objectListFile} { + global srcFileList srcPathList source_extensions + set f [open $objectListFile r] + set fl [read $f] + close $f + + # fix native path seperator so it isn't treated as an escape. + regsub -all {\\} $fl {/} fl + + # Treat the string as a list so filenames between double quotes are + # treated as list elements. + foreach fname $fl { + # Compiled .res resource files should be ignored. + if {[file extension $fname] ne ".obj"} {continue} + + # Just filename without path or extension because the path is + # the build directory, not where the source files are located. + set baseName [file rootname [file tail $fname]] + + set found 0 + foreach path $srcPathList { + foreach ext $source_extensions { + set test [file join $path ${baseName}${ext}] + if {[file exist $test]} { + lappend srcFileList $test + set found 1 + break + } + } + if {$found} break + } + } +} + +# main -- +# +# The main procedure of this script. +# +# Arguments: +# none. +# +# Results: +# None. + +proc main {} { + global argc argv mode mode_data srcFileList srcPathList excludes + global remove_prefix target_prefix output env + + set srcPathList [list] + set srcFileList [list] + + if {$argc == 1} {displayUsage} + + # Parse mkdepend input + for {set i 0} {$i < [llength $argv]} {incr i} { + switch -glob -- [set arg [lindex $argv $i]] { + -vc32 { + set mode vc32 + } + -bc32 { + set mode bc32 + } + -wc32 { + set mode wc32 + } + -lc32 { + set mode lc32 + } + -mgw32 { + set mode mgw32 + } + -passthru:* { + set passthru [string range $arg 10 end] + regsub -all {"} $passthru {\"} passthru + regsub -all {\\} $passthru {/} passthru + } + -out:* { + openOutput [string range $arg 5 end] + } + @* { + set objfile [string range $arg 1 end] + regsub -all {\\} $objfile {/} objfile + readInputListFile $objfile + } + -? - -help - --help { + displayUsage + exit 1 + } + default { + if {![info exist mode]} { + puts stderr "mode not set" + displayUsage + } + addSearchPath $arg + } + } + } + + # Execute the CPP command and parse output + + foreach srcFile $srcFileList { + if {[catch { + set command "$mode_data($mode) $passthru \"$srcFile\"" + set input [open |$command r] + set depends [readDepends $input] + set status [catch {close $input} result] + if {$status == 1 && [lindex $::errorCode 0] eq "CHILDSTATUS"} { + foreach { - pid code } $::errorCode break + if {$code == 2} { + # preprocessor died a cruel death. + error $result + } + } + } err]} { + puts stderr "error ocurred: $err\n" + continue + } + set depends [filterExcludes $depends $excludes] + set depends [rebaseFiles $depends] + set depends [compressDeps $depends] + writeDepends $output $depends + } + + closeOutput +} + +# kick it up. +main |