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 | |
---|
10 | set SCRIPTVERSION 0.1 |
---|
11 | |
---|
12 | array set portsSeen {} |
---|
13 | |
---|
14 | proc 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 | |
---|
29 | package require Tclx |
---|
30 | package require macports |
---|
31 | package require Pextlib 1.0 |
---|
32 | |
---|
33 | package require fileutil::traverse |
---|
34 | |
---|
35 | # fileutil::traverse filter: |
---|
36 | proc 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 | |
---|
46 | fileutil::traverse Trawler . -filter trAccept |
---|
47 | |
---|
48 | # extend a command with a new subcommand |
---|
49 | proc 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 | |
---|
72 | extend string { |
---|
73 | proc cat args { |
---|
74 | join $args "" |
---|
75 | } |
---|
76 | } |
---|
77 | |
---|
78 | set macportsTclPath /Library/Tcl |
---|
79 | set inverse 0 |
---|
80 | set showVersion 0 |
---|
81 | set _WD_port {} |
---|
82 | |
---|
83 | array set ui_options {} |
---|
84 | array set global_options {} |
---|
85 | array set global_variations {} |
---|
86 | |
---|
87 | while {[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 | |
---|
122 | proc 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 | |
---|
158 | proc 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 | |
---|
163 | proc 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 | |
---|
193 | if {[catch {mportinit ui_options global_options global_variations} result]} { |
---|
194 | puts \$::errorInfo |
---|
195 | fatal "Failed to initialise MacPorts, \$result" |
---|
196 | } |
---|
197 | |
---|
198 | if {$showVersion} { |
---|
199 | puts "Version $SCRIPTVERSION" |
---|
200 | puts "MacPorts version [macports::version]" |
---|
201 | exit 0 |
---|
202 | } |
---|
203 | |
---|
204 | if {[llength $::argv] == 0} { |
---|
205 | puts "Error: missing port-name" |
---|
206 | printUsage |
---|
207 | exit 2 |
---|
208 | } |
---|
209 | |
---|
210 | foreach 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 | } |
---|