Skip to content

Commit ef2bc78

Browse files
committed
_activate_contents: find all conflicts in one query
1 parent 9eb441f commit ef2bc78

1 file changed

Lines changed: 116 additions & 43 deletions

File tree

src/registry2.0/portimage.tcl

Lines changed: 116 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,57 @@ proc _progress {args} {
634634
ui_progress_generic {*}${args}
635635
}
636636

637+
proc _get_port_conflicts {port} {
638+
global registry::tdbc_connection
639+
if {![info exists tdbc_connection]} {
640+
registry::tdbc_connect
641+
}
642+
variable conflicts_stmt
643+
if {![info exists conflicts_stmt]} {
644+
set query {SELECT files.id, files.path, files.actual_path FROM
645+
(SELECT path FROM files where id = :thisport_id)
646+
AS thisport_files
647+
INNER JOIN files ON thisport_files.path = files.actual_path
648+
WHERE files.active = 1}
649+
set conflicts_stmt [$tdbc_connection prepare $query]
650+
}
651+
652+
set thisport_id [$port id]
653+
$tdbc_connection transaction {
654+
set results [$conflicts_stmt execute]
655+
}
656+
set id_to_port [dict create]
657+
set path_to_port [dict create]
658+
set port_to_paths [dict create]
659+
set is_replaced [dict create]
660+
set todeactivate [dict create]
661+
set thisport_name [$port name]
662+
foreach result [$results allrows] {
663+
set id [dict get $result id]
664+
if {![dict exists $id_to_port $id]} {
665+
dict set id_to_port $id [lindex [registry::entry search id $id] 0]
666+
}
667+
set conflicting_port [dict get $id_to_port $id]
668+
if {![dict exists $is_replaced $conflicting_port]} {
669+
lassign [mportlookup [$conflicting_port name]] _ portinfo
670+
if {[dict exists $portinfo replaced_by] && [lsearch -exact -nocase [dict get $portinfo replaced_by] $thisport_name] != -1} {
671+
dict set is_replaced $conflicting_port 1
672+
dict set todeactivate $conflicting_port 1
673+
} else {
674+
dict set is_replaced $conflicting_port 0
675+
}
676+
}
677+
set actual_path [dict get $result actual_path]
678+
dict set path_to_port $actual_path $conflicting_port
679+
if {![dict get $is_replaced $conflicting_port]} {
680+
set imagepath [dict get $result path]
681+
dict lappend port_to_paths $conflicting_port [list $imagepath $actual_path]
682+
}
683+
}
684+
$results close
685+
return [list $path_to_port $port_to_paths $todeactivate]
686+
}
687+
637688
## Activates the contents of a port
638689
proc _activate_contents {port {rename_list {}}} {
639690
variable force
@@ -679,7 +730,7 @@ proc _activate_contents {port {rename_list {}}} {
679730
# Last, if the file exists, and belongs to another port, and force is set
680731
# we remove the file from the file_map, take ownership of it, and
681732
# clobber it
682-
set todeactivate [list]
733+
set todeactivate [dict create]
683734
try {
684735
registry::write {
685736
foreach file $imagefiles {
@@ -694,51 +745,73 @@ proc _activate_contents {port {rename_list {}}} {
694745
throw registry::image-error "Image error: Source file $srcfile does not appear to exist. Unable to activate port ${portname}."
695746
}
696747

697-
set owner [registry::entry owner $file]
698-
699-
if {$owner ne {} && $owner ne $port} {
700-
# deactivate conflicting port if it is replaced_by this one
701-
set result [mportlookup [$owner name]]
702-
set portinfo [lindex $result 1]
703-
if {[dict exists $portinfo replaced_by] && [lsearch -exact -nocase [dict get $portinfo replaced_by] $portname] != -1} {
704-
# we'll deactivate the owner later, but before activating our files
705-
lappend todeactivate $owner
706-
set owner "replaced"
707-
}
708-
}
709-
710-
if {$owner ne "replaced"} {
711-
if {$force} {
712-
# if we're forcing the activation, then we move any existing
713-
# files to a backup file, both in the filesystem and in the
714-
# registry
715-
if { ![catch {::file type $file}] } {
716-
set bakfile "${file}${baksuffix}"
717-
_progress intermission
718-
ui_warn "File $file already exists. Moving to: $bakfile."
719-
::file rename -force -- $file $bakfile
720-
lappend backups $file
721-
}
722-
if { $owner ne {} } {
723-
$owner deactivate [list $file]
724-
$owner activate [list $file] [list "${file}${baksuffix}"]
748+
if {![catch {::file type $file}]} {
749+
if {![info exists conflicts_path_to_port]} {
750+
# Check for conflicting ports. 'todeactivate' contains ports replaced by this one,
751+
# which we'll deactivate later, but before activating our files.
752+
lassign [_get_port_conflicts $port] conflicts_path_to_port conflicts_port_to_paths todeactivate
753+
if {!$force && [dict size $conflicts_port_to_paths] > 0} {
754+
set msg "The following ports have active files that conflict with ${portname}'s:\n"
755+
foreach conflicting_port [dict keys $conflicts_port_to_paths] {
756+
append msg "[$conflicting_port name] @[$conflicting_port version]_[$conflicting_port revision][$conflicting_port variants]\n"
757+
set conflicting_paths [dict get $conflicts_port_to_paths $conflicting_port]
758+
set pathcounter 0
759+
set pathtotal [llength $conflicting_paths]
760+
foreach {_ actual_path} $conflicting_paths {
761+
if {$pathcounter >= 3 && $pathtotal > 4} {
762+
append msg " (... [expr {$pathtotal - $pathcounter}] more not shown)\n"
763+
break
764+
}
765+
append msg " ${actual_path}\n"
766+
incr pathcounter
767+
}
768+
}
769+
append msg "Image error: Conflicting file(s) present. Please deactivate the conflicting port(s) first, or use 'port -f activate $portname' to force the activation."
770+
throw registry::image-error $msg
725771
}
772+
}
773+
if {[dict exists $conflicts_path_to_port $file]} {
774+
set owner [dict get $conflicts_path_to_port $file]
726775
} else {
727-
# if we're not forcing the activation, then we bail out if
728-
# we find any files that already exist, or have entries in
729-
# the registry
730-
if { $owner ne {} && $owner ne $port } {
731-
set msg "Image error: $file is being used by the active [$owner name] port. Please deactivate this port first, or use 'port -f activate $portname' to force the activation."
732-
#registry::entry close $owner
733-
throw registry::image-error $msg
734-
} elseif { $owner eq {} && ![catch {::file type $file}] } {
776+
set owner {}
777+
}
778+
if {$owner eq {} || ![dict exists $todeactivate $owner]} {
779+
if {$force} {
780+
# if we're forcing the activation, then we move any existing
781+
# files to a backup file, both in the filesystem and in the
782+
# registry
783+
if {$owner ne {}} {
784+
# Rename all conflicting files for this owner.
785+
set owner_paths [list]
786+
set owner_actual_paths [list]
787+
foreach {path actual_path} [dict get $conflicts_port_to_paths $owner] {
788+
lappend owner_deactivate_paths $path
789+
if {![catch {::file type $actual_path}]} {
790+
lappend owner_activate_paths $path
791+
set bakfile ${actual_path}${baksuffix}
792+
lappend owner_backup_paths $bakfile
793+
ui_warn "File $actual_path already exists. Moving to: $bakfile."
794+
::file rename -force -- $actual_path $bakfile
795+
lappend backups $actual_path
796+
}
797+
}
798+
$owner deactivate $owner_deactivate_paths
799+
$owner activate $owner_activate_paths $owner_backup_paths
800+
} else {
801+
# Just rename this file.
802+
set bakfile ${file}${baksuffix}
803+
_progress intermission
804+
ui_warn "File $file already exists. Moving to: $bakfile."
805+
::file rename -force -- $file $bakfile
806+
lappend backups $file
807+
}
808+
} else {
809+
# if we're not forcing the activation, then we bail out if
810+
# we find any files that already exist
735811
set msg "Image error: $file already exists and does not belong to a registered port. Unable to activate port ${portname}. Use 'port -f activate $portname' to force the activation."
736812
throw registry::image-error $msg
737813
}
738814
}
739-
#if {$owner ne {}} {
740-
# registry::entry close $owner
741-
#}
742815
}
743816

744817
# Split out the filename's subpaths and add them to the
@@ -775,7 +848,7 @@ proc _activate_contents {port {rename_list {}}} {
775848

776849
# deactivate ports replaced_by this one
777850
set deactivate_options [dict create ports_nodepcheck 1]
778-
foreach owner $todeactivate {
851+
foreach owner [dict keys $todeactivate] {
779852
_progress intermission
780853
if {$noexec || ![registry::run_target $owner deactivate $deactivate_options]} {
781854
deactivate [$owner name] "" "" 0 $deactivate_options
@@ -860,7 +933,7 @@ proc _activate_contents {port {rename_list {}}} {
860933
::file rename -force -- "${file}${baksuffix}" $file
861934
}
862935
# reactivate deactivated ports
863-
foreach entry $todeactivate {
936+
foreach entry [dict keys $todeactivate] {
864937
if {[$entry state] eq "imaged" && ($noexec || ![registry::run_target $entry activate ""])} {
865938
activate [$entry name] [$entry version] [$entry revision] [$entry variants] [dict create ports_activate_no-exec $noexec]
866939
}
@@ -873,7 +946,7 @@ proc _activate_contents {port {rename_list {}}} {
873946

874947
throw [dict get $eOptions -errorcode] [dict get $eOptions -errorinfo]
875948
} finally {
876-
#foreach entry $todeactivate {
949+
#foreach entry [dict keys $todeactivate] {
877950
# registry::entry close $entry
878951
#}
879952
# Only delete extracted dir if there is an archive to re-extract from

0 commit comments

Comments
 (0)