Ticket #54315: port-check-conflicts.tcl

File port-check-conflicts.tcl, 8.5 KB (added by RJVB (René Bertin), 7 years ago)
Line 
1#!/usr/bin/env port-tclsh
2# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
3#
4# check if a port's destroot directory contains already installed files or (with -v) list new files
5#
6# parts copied from Clemens Lang's port-check-distributable.tcl script and of course the `port` driver command
7#
8# everything else (c) 2017 R.J.V. Bertin
9
10set SCRIPTVERSION 0.1
11
12array set portsSeen {}
13
14proc printUsage {} {
15    puts "Usage: $::argv0 \[-vV\] \[-t macports-tcl-path\] port-name\[s\]"
16    puts "  -h    This help"
17    puts "  -d    some debug output"
18    puts "  -v    list new files (inVerse mode)"
19    puts "  -V    show version and MacPorts version being used"
20    puts ""
21    puts "port-name\[s\] is the name of a port(s) to check"
22    puts "port-name can also be the path to a destroot directory"
23    puts "  (for checking projects that are not yet available as a port)"
24}
25
26
27# Begin
28
29package require Tclx
30package require macports
31package require Pextlib 1.0
32
33package require fileutil::traverse
34
35# fileutil::traverse filter:
36proc trAccept {path} {
37    set ftype [file type ${path}]
38    if {![string equal ${ftype} "directory"]} {
39        ui_debug "${path} : accepting ${ftype}"
40        return 1
41    } else {
42        return 0
43    }
44}
45
46fileutil::traverse Trawler . -filter trAccept
47
48# extend a command with a new subcommand
49proc extend {cmd body} {
50    if {![namespace exists ${cmd}]} {
51        set wrapper [string map [list %C $cmd %B $body] {
52            namespace eval %C {}
53            rename %C %C::%C
54            namespace eval %C {
55                proc _unknown {junk subc args} {
56                    return [list %C::%C $subc]
57                }
58                namespace ensemble create -unknown %C::_unknown
59            }
60        }]
61    }
62
63    append wrapper [string map [list %C $cmd %B $body] {
64        namespace eval %C {
65            %B
66            namespace export -clear *
67        }
68    }]
69    uplevel 1 $wrapper
70}
71
72extend string {
73    proc cat args {
74        join $args ""
75    }
76}
77
78set macportsTclPath /Library/Tcl
79set inverse 0
80set showVersion 0
81set _WD_port {}
82
83array set ui_options        {}
84array set global_options    {}
85array set global_variations {}
86
87while {[string index [lindex $::argv 0] 0] == "-" } {
88    switch [string range [lindex $::argv 0] 1 end] {
89        h {
90            printUsage
91            exit 0
92        }
93        d {
94            set ui_options(ports_debug) yes
95            # debug implies verbose
96            set ui_options(ports_verbose) yes
97        }
98        t {
99            if {[llength $::argv] < 2} {
100                puts "-t needs a path"
101                printUsage
102                exit 2
103            }
104            set macportsTclPath [lindex $::argv 1]
105            set ::argv [lrange $::argv 1 end]
106        }
107        v {
108             set inverse 1
109        }
110        V {
111            set showVersion 1
112        }
113        default {
114            puts "Unknown option [lindex $::argv 0]"
115            printUsage
116            exit 2
117        }
118    }
119    set ::argv [lrange $::argv 1 end]
120}
121
122proc port_workdir {portname} {
123    # Operations on the port's directory and Portfile
124    global env boot_env current_portdir
125
126    set status 0
127
128    array unset portinfo
129
130    # Verify the portname, getting portinfo to map to a porturl
131    if {[catch {set res [mportlookup $portname]} result]} {
132        ui_debug $::errorInfo
133        ui_error "lookup of portname $portname failed: $result"
134        return ""
135    }
136    if {[llength $res] < 2} {
137        ui_error "Port $portname not found"
138        return ""
139    }
140    array set portinfo [lindex $res 1]
141    set porturl $portinfo(porturl)
142    set portname $portinfo(name)
143
144
145    # Calculate portdir, porturl, and portfile from initial porturl
146    set portdir [file normalize [macports::getportdir $porturl]]
147    set porturl "file://${portdir}";    # Rebuild url so it's fully qualified
148    set portfile "${portdir}/Portfile"
149    # output the path to the port's work directory
150    set workpath [macports::getportworkpath_from_portdir $portdir $portname]
151    if {[file exists $workpath]} {
152        return $workpath
153    } else {
154        return ""
155    }
156}
157
158proc macports::normalise { filename } {
159    set prefmap [list [file dirname [file normalize "${macports::prefix}/foo"]] ${macports::prefix}]
160    return [string map ${prefmap} [file normalize $filename]]
161}
162
163proc port_provides { fileNames } {
164    # In this case, portname is going to be used for the filename... since
165    # that is the first argument we expect... perhaps there is a better way
166    # to do this?
167    if { ![llength $fileNames] } {
168        ui_error "Please specify a filename to check which port provides that file."
169        return 1
170    }
171    foreach filename $fileNames {
172        set file [macports::normalise $filename]
173        if {[file exists $file] || ![catch {file type $file}]} {
174            if {![file isdirectory $file] || [file type $file] eq "link"} {
175                set port [registry::file_registered $file]
176                if { $port != 0 } {
177                    dict set providers "${filename}" "${port}"
178                } else {
179                    dict set providers "${filename}" "not_by_MacPorts"
180                }
181            } else {
182                dict set providers "${filename}" "is_a_directory"
183            }
184        } else {
185            dict set providers "${filename}" "does_not_exist"
186        }
187    }
188    registry::close_file_map
189
190    return ${providers}
191}
192
193if {[catch {mportinit ui_options global_options global_variations} result]} {
194    puts \$::errorInfo
195        fatal "Failed to initialise MacPorts, \$result"
196}
197
198if {$showVersion} {
199    puts "Version $SCRIPTVERSION"
200    puts "MacPorts version [macports::version]"
201    exit 0
202}
203
204if {[llength $::argv] == 0} {
205    puts "Error: missing port-name"
206    printUsage
207    exit 2
208}
209
210foreach portName $::argv {
211    set pWD ""
212    set OK 0
213    if {[file exists ${portName}] && [file type ${portName}] eq "directory"} {
214        # we're pointed to a directory
215        set pWD ${portName}
216        cd ${pWD}
217        set OK 1
218        ui_msg "Checking in directory ${pWD}"
219    } elseif {${_WD_port} ne ${portName}} {
220        set _WD_port ${portName}
221        set pWD [port_workdir ${portName}]
222        ui_msg "Checking port:${portName}: ${pWD}"
223        if {[file exists "${pWD}/destroot"]} {
224            cd "${pWD}/destroot"
225            set OK 1
226        }
227    }
228    if {${pWD} ne ""} {
229        if {${OK}} {
230            set FILES {}
231            ui_debug "Building file list for ${portName}"
232            Trawler foreach file {
233                set FILES [lappend FILES "${file}"]
234            }
235            set InstalledDupsList {}
236            set DestrootDupsList {}
237            if {${inverse}} {
238                ui_debug "Checking [llength ${FILES}] files for already installed copies"
239            } else {
240                ui_debug "Checking [llength ${FILES}] files for new, not-yet-installed items"
241            }
242            foreach f $FILES {
243                set g [string range ${f} 1 end]
244                if {[file exists "${g}"]} {
245                    if {!${inverse}} {
246                        set InstalledDupsList [lappend InstalledDupsList "${g}"]
247                        set DestrootDupsList [lappend DestrootDupsList "${f}"]
248                    }
249                } elseif {${inverse}} {
250                    regsub -all {[ \r\t\n]+} ${g} "" gg
251                    if {${g} ne ${gg}} {
252                        puts "\"${g}\" doesn't exist yet"
253                    } else {
254                        puts "${g} doesn't exist yet"
255                    }
256                }
257            }
258            if {[llength ${InstalledDupsList}]} {
259                ui_msg "[llength ${InstalledDupsList}] files already exist, checking if any do not already belong to ${portName}"
260                set ProviderDict [port_provides ${InstalledDupsList}]
261                set DUPS {}
262                dict for {g provider} ${ProviderDict} {
263                    if {${provider} ne ${portName}} {
264                        regsub -all {[ \r\t\n]+} ${g} "" gg
265                        if {${g} ne ${gg}} {
266                            puts "\"${g}\" already exists"
267                        } else {
268                            puts "${g} already exists"
269                        }
270                        puts "\tprovided by: ${provider}"
271                        system "ls -l \"./${g}\" \"${g}\""
272                        set DUPS [lappend DUPS [string cat "${g}" "\n"]]
273                    }
274                }
275                if {[llength ${DUPS}]} {
276                    puts [join ${DUPS}]
277                }
278            }
279        }
280    }
281}