diff --git a/src/port1.0/Makefile.in b/src/port1.0/Makefile.in index b837362d5..e1efc2dd0 100644 --- a/src/port1.0/Makefile.in +++ b/src/port1.0/Makefile.in @@ -16,7 +16,10 @@ SRCS= port.tcl portchecksum.tcl portconfigure.tcl portextract.tcl \ portactivate_run.tcl portbuild_run.tcl portbump_run.tcl portchecksum_run.tcl \ portclean_run.tcl portconfigure_run.tcl portdeactivate_run.tcl \ portdestroot_run.tcl portdistcheck_run.tcl portdistfiles_run.tcl \ - portfetch_run.tcl + portextract_run.tcl portfetch_run.tcl portinstall_run.tcl portlint_run.tcl \ + portlivecheck_run.tcl portload_run.tcl portmirror_run.tcl \ + portpatch_run.tcl portreload_run.tcl portstartupitem_run.tcl \ + porttest_run.tcl portuninstall_run.tcl portunload_run.tcl include $(srcdir)/../../Mk/macports.subdir.mk diff --git a/src/port1.0/port.tcl b/src/port1.0/port.tcl index 1ec2de33c..306a15095 100644 --- a/src/port1.0/port.tcl +++ b/src/port1.0/port.tcl @@ -79,7 +79,6 @@ package require portdeactivate 1.0 package require portclean 1.0 package require porttest 1.0 package require portlint 1.0 -package require porttrace 1.0 package require portdistcheck 1.0 package require portlivecheck 1.0 package require portmirror 1.0 @@ -91,4 +90,3 @@ package require portunload 1.0 package require portreload 1.0 package require portdistfiles 1.0 -package require portsandbox 1.0 diff --git a/src/port1.0/portactivate.tcl b/src/port1.0/portactivate.tcl index 191ea73a6..6d6d7d99c 100644 --- a/src/port1.0/portactivate.tcl +++ b/src/port1.0/portactivate.tcl @@ -1,5 +1,7 @@ # -*- 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 +# License: see portactivate_run.tcl + # the 'activate' target is provided by this package package provide portactivate 1.0 diff --git a/src/port1.0/portactivate_run.tcl b/src/port1.0/portactivate_run.tcl index 586eb7dde..9e85b3324 100644 --- a/src/port1.0/portactivate_run.tcl +++ b/src/port1.0/portactivate_run.tcl @@ -31,7 +31,6 @@ package provide portactivate_run 1.0 package require portstartupitem 1.0 -package require portutil 1.0 namespace eval portactivate { diff --git a/src/port1.0/portbuild.tcl b/src/port1.0/portbuild.tcl index 709487866..2ab981f5b 100644 --- a/src/port1.0/portbuild.tcl +++ b/src/port1.0/portbuild.tcl @@ -1,5 +1,4 @@ -# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4 -# portbuild.tcl +# -*- 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 # # Copyright (c) 2007 - 2013 The MacPorts Project # Copyright (c) 2002 - 2004 Apple Inc. @@ -28,7 +27,6 @@ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -# package provide portbuild 1.0 diff --git a/src/port1.0/portbuild_run.tcl b/src/port1.0/portbuild_run.tcl index a627aa997..a53f4c8c7 100644 --- a/src/port1.0/portbuild_run.tcl +++ b/src/port1.0/portbuild_run.tcl @@ -1,3 +1,7 @@ +# -*- 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 + +# License: see portbuild.tcl + package provide portbuild_run 1.0 package require portutil 1.0 package require portprogress 1.0 diff --git a/src/port1.0/portbump.tcl b/src/port1.0/portbump.tcl index 2cad1cae5..5e8bf62d3 100644 --- a/src/port1.0/portbump.tcl +++ b/src/port1.0/portbump.tcl @@ -1,5 +1,7 @@ # -*- 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 +# License: see portbump_run.tcl + package provide portbump 1.0 set org.macports.bump [target_new org.macports.bump portbump::bump_main] diff --git a/src/port1.0/portchecksum.tcl b/src/port1.0/portchecksum.tcl index 919031e0b..b924ebf1e 100644 --- a/src/port1.0/portchecksum.tcl +++ b/src/port1.0/portchecksum.tcl @@ -1,5 +1,7 @@ # -*- 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 +# License: see portchecksum_run.tcl + package provide portchecksum 1.0 set org.macports.checksum [target_new org.macports.checksum portchecksum::checksum_main] diff --git a/src/port1.0/portclean.tcl b/src/port1.0/portclean.tcl index 4b4a3b7c7..25c17e018 100644 --- a/src/port1.0/portclean.tcl +++ b/src/port1.0/portclean.tcl @@ -1,5 +1,7 @@ # -*- 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 +# License: see portclean_run.tcl + # the 'clean' target is provided by this package package provide portclean 1.0 diff --git a/src/port1.0/portconfigure.tcl b/src/port1.0/portconfigure.tcl index 7bc5e6687..c47addd01 100644 --- a/src/port1.0/portconfigure.tcl +++ b/src/port1.0/portconfigure.tcl @@ -1,4 +1,4 @@ -# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4 +# -*- 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 # # Copyright (c) 2007 - 2020 The MacPorts Project # Copyright (c) 2007 Markus W. Weissmann @@ -28,7 +28,6 @@ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -# package provide portconfigure 1.0 diff --git a/src/port1.0/portconfigure_run.tcl b/src/port1.0/portconfigure_run.tcl index aadc9acbf..944cd368e 100644 --- a/src/port1.0/portconfigure_run.tcl +++ b/src/port1.0/portconfigure_run.tcl @@ -1,5 +1,7 @@ # -*- 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 +# License: see portconfigure.tcl + package provide portconfigure_run 1.0 package require portutil 1.0 package require portprogress 1.0 diff --git a/src/port1.0/portdeactivate.tcl b/src/port1.0/portdeactivate.tcl index b54086061..6447dc1e0 100644 --- a/src/port1.0/portdeactivate.tcl +++ b/src/port1.0/portdeactivate.tcl @@ -1,5 +1,7 @@ # -*- 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 +# License: see portdeactivate_run.tcl + # the 'deactivate' target is provided by this package package provide portdeactivate 1.0 diff --git a/src/port1.0/portdeactivate_run.tcl b/src/port1.0/portdeactivate_run.tcl index 59ce2d1b9..4bcba463a 100644 --- a/src/port1.0/portdeactivate_run.tcl +++ b/src/port1.0/portdeactivate_run.tcl @@ -28,7 +28,7 @@ # POSSIBILITY OF SUCH DAMAGE. package provide portdeactivate_run 1.0 -package require portutil 1.0 +package require portstartupitem_run 1.0 namespace eval portdeactivate { diff --git a/src/port1.0/portdestroot.tcl b/src/port1.0/portdestroot.tcl index 644fbc4cf..609c0e836 100644 --- a/src/port1.0/portdestroot.tcl +++ b/src/port1.0/portdestroot.tcl @@ -1,5 +1,7 @@ # -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4 +# License: see portdestroot_run.tcl + package provide portdestroot 1.0 set org.macports.destroot [target_new org.macports.destroot portdestroot::destroot_main] diff --git a/src/port1.0/portdestroot_run.tcl b/src/port1.0/portdestroot_run.tcl index 4a6aa37ba..e580cba4c 100644 --- a/src/port1.0/portdestroot_run.tcl +++ b/src/port1.0/portdestroot_run.tcl @@ -30,7 +30,8 @@ # POSSIBILITY OF SUCH DAMAGE. package provide portdestroot_run 1.0 -package require portutil 1.0 +package require portprogress 1.0 +package require portstartupitem_run 1.0 namespace eval portdestroot { @@ -45,7 +46,6 @@ proc destroot_start {args} { ui_notice "$UI_PREFIX [format [msgcat::mc "Staging %s into destroot"] ${subport}]" - # start gsoc08-privileges if { [getuid] == 0 && [geteuid] != 0 } { # if started with sudo but have dropped the privileges ui_debug "Can't run destroot under sudo without elevated privileges (due to mtree)." @@ -60,8 +60,6 @@ proc destroot_start {args} { return -code error "You cannot run this port without root privileges. You need to re-run with 'sudo port'."; } - # end gsoc08-privileges - set oldmask [umask ${destroot.umask}] set mtree [findBinary mtree ${::portutil::autoconf::mtree_path}] diff --git a/src/port1.0/portdistcheck.tcl b/src/port1.0/portdistcheck.tcl index 0185dd5f2..667430501 100644 --- a/src/port1.0/portdistcheck.tcl +++ b/src/port1.0/portdistcheck.tcl @@ -1,5 +1,7 @@ # -*- 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 +# License: see portdistcheck_run.tcl + package provide portdistcheck 1.0 set org.macports.distcheck [target_new org.macports.distcheck portdistcheck::distcheck_main] diff --git a/src/port1.0/portdistfiles.tcl b/src/port1.0/portdistfiles.tcl index 88c73be1b..c8c8a0341 100644 --- a/src/port1.0/portdistfiles.tcl +++ b/src/port1.0/portdistfiles.tcl @@ -1,5 +1,7 @@ # -*- 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 +# License: see portdistfiles_run.tcl + package provide portdistfiles 1.0 set org.macports.distfiles [target_new org.macports.distfiles portdistfiles::distfiles_main] diff --git a/src/port1.0/portextract.tcl b/src/port1.0/portextract.tcl index 973156851..e8bc49c0c 100644 --- a/src/port1.0/portextract.tcl +++ b/src/port1.0/portextract.tcl @@ -1,5 +1,4 @@ -# et:ts=4 -# portextract.tcl +# -*- 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 # # Copyright (c) 2005, 2007-2011, 2013-2014, 2016, 2018 The MacPorts Project # Copyright (c) 2002 - 2003 Apple Inc. @@ -29,16 +28,14 @@ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -# package provide portextract 1.0 -package require portutil 1.0 -package require port 1.0 set org.macports.extract [target_new org.macports.extract portextract::extract_main] target_provides ${org.macports.extract} extract target_requires ${org.macports.extract} main fetch checksum target_prerun ${org.macports.extract} portextract::extract_start +target_runpkg ${org.macports.extract} portextract_run namespace eval portextract { variable all_use_options [list use_7z use_bzip2 use_dmg use_lzip use_lzma use_tar use_xz use_zip] @@ -54,7 +51,6 @@ commands extract # Set up defaults default extract.asroot no -# XXX call out to code in portutil.tcl XXX # This cleans the distfiles list of all site tags default extract.only {[portextract::disttagclean $distfiles]} @@ -73,8 +69,6 @@ foreach _extract_use_option ${portextract::all_use_options} { } unset _extract_use_option -set_ui_prefix - # Map a given file name to a canonical extract method name proc portextract::method_for_suffix {filename} { switch -glob -nocase -- $filename { @@ -314,123 +308,8 @@ proc portextract::add_extract_deps {} { } port::register_callback portextract::add_extract_deps -# XXX -# Helper function for portextract.tcl that strips all tag names from a list +# Helper function that strips all tag names from a list # Used to clean ${distfiles} for setting the ${extract.only} default proc portextract::disttagclean {list} { return [lmap fname $list {getdistname $fname}] } - -proc portextract::extract_start {args} { - global UI_PREFIX extract.dir extract.mkdir - - ui_notice "$UI_PREFIX [format [msgcat::mc "Extracting %s"] [option subport]]" - - # create any users and groups needed by the port - handle_add_users - - # should the distfiles be extracted to worksrcpath instead? - if {[tbool extract.mkdir]} { - global worksrcpath - ui_debug "Extracting to subdirectory worksrcdir" - file mkdir ${worksrcpath} - set extract.dir ${worksrcpath} - } - - variable methods_used - if {[dict exists $methods_used dmg]} { - variable dmg_mount [mkdtemp "/tmp/mports.XXXXXXXX"] - } -} - -proc portextract::extract_main {args} { - global UI_PREFIX distpath filespath extract.dir extract.only extract.methods \ - extract.cmd extract.pre_args extract.post_args extract.suffix - - if {![exists distfiles] && ![exists extract.only]} { - # nothing to do - return 0 - } - - # extract.{cmd,pre_args,post_args} options are used for distfiles - # using the method matching extract.suffix. This keeps the - # behaviour the same as it used to be for the case where all - # distfiles in extract.only use that method, which was formerly the - # only supported case. - # For files with different methods, the args are set up as per the - # defaults for the method. If custom args are needed in this case, - # a custom method should be used. - set main_method [method_for_suffix ${extract.suffix}] - foreach distfile ${extract.only} { - ui_info "$UI_PREFIX [format [msgcat::mc "Extracting %s"] $distfile]" - if {[file exists $filespath/$distfile]} { - option extract.args "'$filespath/$distfile'" - } else { - option extract.args "'[file join $distpath $distfile]'" - } - - if {[dict exists ${extract.methods} $distfile]} { - set method [dict get ${extract.methods} $distfile] - } else { - set method [method_for_suffix $distfile] - } - ui_debug "Using extract method: $method" - - if {$method ne $main_method} { - if {![info exists saved_options]} { - set saved_options [list ${extract.cmd} ${extract.pre_args} ${extract.post_args}] - } - # set up the args for this method - set extract.cmd [get_extract_cmd $method] - set extract.pre_args [get_extract_pre_args $method] - set extract.post_args [get_extract_post_args $method] - } - # If the MacPorts user does not have the privileges to mount a - # DMG then hdiutil will fail with this error: - # hdiutil: attach failed - Device not configured - # So elevate back to root. - if {$method eq "dmg"} { - elevateToRoot {extract dmg} - } - - if {${extract.cmd} ne {}} { - # built-in method - set code [catch {command_exec extract} result] - } else { - # custom method, call it as a command - set code [catch {$method [file join $distpath $distfile]} result] - } - - if {$method eq "dmg"} { - dropPrivileges - } - if {$method ne $main_method} { - lassign $saved_options extract.cmd extract.pre_args extract.post_args - } - if {$code} { - return -code error "$result" - } - - chownAsRoot ${extract.dir} - } - - if {[option extract.rename] && ![file exists [option worksrcpath]]} { - global workpath distname - # rename whatever directory exists in $workpath to $distname - set worksubdirs [glob -nocomplain -types d -directory $workpath *] - if {[llength $worksubdirs] == 1} { - set origpath [lindex $worksubdirs 0] - set newpath [file join $workpath $distname] - if {$newpath ne $origpath} { - ui_debug [format [msgcat::mc "extract.rename: Renaming %s -> %s"] [file tail $origpath] $distname] - move $origpath $newpath - } - } elseif {[llength $worksubdirs] == 0} { - return -code error "extract.rename: no directories exist in $workpath" - } else { - return -code error "extract.rename: multiple directories exist in ${workpath}: $worksubdirs" - } - } - - return 0 -} diff --git a/src/port1.0/portextract_run.tcl b/src/port1.0/portextract_run.tcl new file mode 100644 index 000000000..84592509c --- /dev/null +++ b/src/port1.0/portextract_run.tcl @@ -0,0 +1,123 @@ +# -*- 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 + +# License: see portextract.tcl + +package provide portextract_run 1.0 + +namespace eval portextract { + +proc extract_start {args} { + global UI_PREFIX extract.dir extract.mkdir + + ui_notice "$UI_PREFIX [format [msgcat::mc "Extracting %s"] [option subport]]" + + # create any users and groups needed by the port + handle_add_users + + # should the distfiles be extracted to worksrcpath instead? + if {[tbool extract.mkdir]} { + global worksrcpath + ui_debug "Extracting to subdirectory worksrcdir" + file mkdir ${worksrcpath} + set extract.dir ${worksrcpath} + } + + variable methods_used + if {[dict exists $methods_used dmg]} { + variable dmg_mount [mkdtemp "/tmp/mports.XXXXXXXX"] + } +} + +proc extract_main {args} { + global UI_PREFIX distpath filespath extract.dir extract.only extract.methods \ + extract.cmd extract.pre_args extract.post_args extract.suffix + + if {![exists distfiles] && ![exists extract.only]} { + # nothing to do + return 0 + } + + # extract.{cmd,pre_args,post_args} options are used for distfiles + # using the method matching extract.suffix. This keeps the + # behaviour the same as it used to be for the case where all + # distfiles in extract.only use that method, which was formerly the + # only supported case. + # For files with different methods, the args are set up as per the + # defaults for the method. If custom args are needed in this case, + # a custom method should be used. + set main_method [method_for_suffix ${extract.suffix}] + foreach distfile ${extract.only} { + ui_info "$UI_PREFIX [format [msgcat::mc "Extracting %s"] $distfile]" + if {[file exists $filespath/$distfile]} { + option extract.args "'$filespath/$distfile'" + } else { + option extract.args "'[file join $distpath $distfile]'" + } + + if {[dict exists ${extract.methods} $distfile]} { + set method [dict get ${extract.methods} $distfile] + } else { + set method [method_for_suffix $distfile] + } + ui_debug "Using extract method: $method" + + if {$method ne $main_method} { + if {![info exists saved_options]} { + set saved_options [list ${extract.cmd} ${extract.pre_args} ${extract.post_args}] + } + # set up the args for this method + set extract.cmd [get_extract_cmd $method] + set extract.pre_args [get_extract_pre_args $method] + set extract.post_args [get_extract_post_args $method] + } + # If the MacPorts user does not have the privileges to mount a + # DMG then hdiutil will fail with this error: + # hdiutil: attach failed - Device not configured + # So elevate back to root. + if {$method eq "dmg"} { + elevateToRoot {extract dmg} + } + + if {${extract.cmd} ne {}} { + # built-in method + set code [catch {command_exec extract} result] + } else { + # custom method, call it as a command + set code [catch {$method [file join $distpath $distfile]} result] + } + + if {$method eq "dmg"} { + dropPrivileges + } + if {$method ne $main_method} { + lassign $saved_options extract.cmd extract.pre_args extract.post_args + } + if {$code} { + return -code error "$result" + } + + chownAsRoot ${extract.dir} + } + + if {[option extract.rename] && ![file exists [option worksrcpath]]} { + global workpath distname + # rename whatever directory exists in $workpath to $distname + set worksubdirs [glob -nocomplain -types d -directory $workpath *] + if {[llength $worksubdirs] == 1} { + set origpath [lindex $worksubdirs 0] + set newpath [file join $workpath $distname] + if {$newpath ne $origpath} { + ui_debug [format [msgcat::mc "extract.rename: Renaming %s -> %s"] [file tail $origpath] $distname] + move $origpath $newpath + } + } elseif {[llength $worksubdirs] == 0} { + return -code error "extract.rename: no directories exist in $workpath" + } else { + return -code error "extract.rename: multiple directories exist in ${workpath}: $worksubdirs" + } + } + + return 0 +} + +} diff --git a/src/port1.0/portfetch.tcl b/src/port1.0/portfetch.tcl index e4965a301..9ea133c76 100644 --- a/src/port1.0/portfetch.tcl +++ b/src/port1.0/portfetch.tcl @@ -1,33 +1,6 @@ # -*- 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 -# -# Copyright (c) 2004 - 2014, 2016-2018 The MacPorts Project -# Copyright (c) 2002 - 2003 Apple Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of Apple Inc. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# + +# License: see portfetch_run.tcl package provide portfetch 1.0 package require portextract 1.0 diff --git a/src/port1.0/portfetch_run.tcl b/src/port1.0/portfetch_run.tcl index db39ce362..817e62463 100644 --- a/src/port1.0/portfetch_run.tcl +++ b/src/port1.0/portfetch_run.tcl @@ -1,4 +1,32 @@ # -*- 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 +# +# Copyright (c) 2004 - 2014, 2016-2018 The MacPorts Project +# Copyright (c) 2002 - 2003 Apple Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Apple Inc. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. # Code needed to run the fetch target diff --git a/src/port1.0/portinstall.tcl b/src/port1.0/portinstall.tcl index fc5026aef..9f30921e8 100644 --- a/src/port1.0/portinstall.tcl +++ b/src/port1.0/portinstall.tcl @@ -1,393 +1,18 @@ -# et:ts=4 -# portinstall.tcl -# -# Copyright (c) 2002 - 2004 Apple Inc. -# Copyright (c) 2004 Robert Shaw -# Copyright (c) 2005, 2007 - 2012 The MacPorts Project -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of Apple Inc. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# +# -*- 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 + +# License: see portinstall_run.tcl package provide portinstall 1.0 -package require portutil 1.0 set org.macports.install [target_new org.macports.install portinstall::install_main] target_provides ${org.macports.install} install target_runtype ${org.macports.install} always target_requires ${org.macports.install} main archivefetch fetch checksum extract patch configure build destroot target_prerun ${org.macports.install} portinstall::install_start - -namespace eval portinstall { -} +target_runpkg ${org.macports.install} portinstall_run # define options options install.asroot # Set defaults default install.asroot no - -set_ui_prefix - -# If the given path is in a git checkout, return the currently checked -# out commit. If not, return an empty string. -proc portinstall::get_path_commit {path} { - set result "" - if {![catch {findBinary git} git] && ![catch {file type $path} ftype]} { - if {$ftype ne "directory"} { - set path [file dirname $path] - } - # Recent git refuses to run if the current user doesn't own - # the checkout. - if {[getuid] == 0} { - macports_try -pass_signal { - set prev_euid [geteuid] - set prev_egid [getegid] - if {[geteuid] != 0} { - seteuid 0 - } - # Must change egid before dropping root euid. - setegid [name_to_gid [file attributes $path -group]] - seteuid [name_to_uid [file attributes $path -owner]] - } on error {err} { - ui_debug "get_path_commit: dropping privileges failed: $err" - } - } - if {[catch {exec -ignorestderr $git -C $path rev-parse HEAD 2> /dev/null} result]} { - ui_debug "get_path_commit: git rev-parse failed: $result" - set result "" - } - } - if {[info exists prev_euid]} { - seteuid 0 - if {[info exists prev_egid]} { - setegid $prev_egid - } - seteuid $prev_euid - } - return $result -} - -proc portinstall::install_start {args} { - global UI_PREFIX subport version revision portvariants \ - prefix_frozen - ui_notice "$UI_PREFIX [format [msgcat::mc "Installing %s @%s_%s%s"] $subport $version $revision $portvariants]" - - # start gsoc08-privileges - if {![file writable $prefix_frozen] || ([getuid] == 0 && [geteuid] != 0)} { - # if install location is not writable, need root privileges to install - # Also elevate if started as root, since 'file writable' doesn't seem - # to take euid into account. - elevateToRoot "install" - } - # end gsoc08-privileges - - # create any users and groups needed by the port - handle_add_users -} - -proc portinstall::create_archive {location archive.type} { - global workpath destpath portpath subport version revision portvariants \ - archive.dir epoch configure.cxx_stdlib cxx_stdlib PortInfo \ - depends_lib depends_run xcodeversion xcodecltversion use_xcode \ - os.subplatform os.version macos_version source_date_epoch - - portarchive::archive_command_setup ${location} ${archive.type} - set archive.dir ${destpath} - - set archive.fulldestpath [file dirname $location] - # Create archive destination path (if needed) - if {![file isdirectory ${archive.fulldestpath}]} { - file mkdir ${archive.fulldestpath} - } - - # Create (if no files) destroot for archiving - if {![file isdirectory ${destpath}]} { - return -code error "no destroot found at: ${destpath}" - } - - # Copy state file into destroot for archiving - # +STATE contains a copy of the MacPorts state information - set statefile [file join $workpath .macports.${subport}.state] - file copy -force $statefile [file join $destpath "+STATE"] - - # Copy Portfile into destroot for archiving - # +PORTFILE contains a copy of the MacPorts Portfile - set portfile [file join $portpath Portfile] - file copy -force $portfile [file join $destpath "+PORTFILE"] - - # Create some informational files that we don't really use just yet, - # but we may in the future in order to allow port installation from - # archives without a full "ports" tree of Portfiles. - # - # Note: These have been modeled after FreeBSD type package files to - # start. We can change them however we want for actual future use if - # needed. - # - # +COMMENT contains the port description - set fd [open [file join $destpath "+COMMENT"] w] - if {[exists description]} { - puts $fd "[option description]" - } - close $fd - # +DESC contains the port long_description and homepage - set fd [open [file join $destpath "+DESC"] w] - if {[exists long_description]} { - puts $fd "[option long_description]" - } - if {[exists homepage]} { - puts $fd "\nWWW: [option homepage]" - } - close $fd - # +CONTENTS contains the port version/name info and all installed - # files and checksums - set control [list] - set fd [open [file join $destpath "+CONTENTS"] w] - puts $fd "@name ${subport}-${version}_${revision}${portvariants}" - puts $fd "@portname ${subport}" - puts $fd "@portepoch ${epoch}" - puts $fd "@portversion ${version}" - puts $fd "@portrevision ${revision}" - puts $fd "@archs [get_canonical_archs]" - set ourvariations $PortInfo(active_variants) - set vlist [lsort -ascii [dict keys $ourvariations]] - foreach v $vlist { - if {[dict get $ourvariations $v] eq "+"} { - puts $fd "@portvariant +${v}" - } - } - - foreach key [list depends_lib depends_run] { - if {[info exists $key]} { - foreach depspec [set $key] { - set depname [lindex [split $depspec :] end] - set dep [mport_lookup $depname] - if {[llength $dep] < 2} { - ui_debug "Dependency $depname not found" - } else { - lassign $dep depname dep_portinfo - set depver [dict get $dep_portinfo version] - set deprev [dict get $dep_portinfo revision] - puts $fd "@pkgdep ${depname}-${depver}_${deprev}" - } - } - } - } - - puts $fd "@macports_version [macports_version]" - lassign [_get_compatible_platform] compat_platform compat_major - if {$compat_platform ne "any"} { - if {${os.subplatform} ne ""} { - puts $fd "@os.subplatform ${os.subplatform}" - } - if {$compat_major ne "any"} { - puts $fd "@os.version ${os.version}" - if {$macos_version ne ""} { - puts $fd "@macos_version $macos_version" - } - if {$use_xcode && $xcodeversion ni {"" none}} { - puts $fd "@xcodeversion $xcodeversion" - } elseif {$xcodecltversion ni {"" none}} { - puts $fd "@xcodecltversion $xcodecltversion" - } - } - } - set ports_commit [get_path_commit $portpath] - if {$ports_commit ne ""} { - puts $fd "@ports_commit $ports_commit" - } - puts $fd "@source_date_epoch $source_date_epoch" - - set have_fileIsBinary [expr {[option os.platform] eq "darwin"}] - set binary_files [list] - variable file_is_binary [dict create] - # also save the contents for our own use later - variable installPlist [list] - set destpathLen [string length $destpath] - fs-traverse -depth fullpath [list $destpath] { - if {[file type $fullpath] eq "directory"} { - continue - } - - set relpath [string range $fullpath $destpathLen+1 end] - if {[string index $relpath 0] ne "+"} { - puts $fd "$relpath" - set abspath [file join [file separator] $relpath] - lappend installPlist $abspath - if {[file isfile $fullpath]} { - ui_debug "checksum file: $fullpath" - set checksum [md5 file $fullpath] - puts $fd "@comment MD5:$checksum" - if {$have_fileIsBinary} { - # test if (mach-o) binary - set is_binary [fileIsBinary $fullpath] - if {$is_binary} { - lappend binary_files $fullpath - } - puts $fd "@comment binary:$is_binary" - dict set file_is_binary $abspath $is_binary - } - } - } else { - lappend control $relpath - } - } - foreach relpath $control { - puts $fd "@ignore" - puts $fd "$relpath" - } - variable actual_cxx_stdlib [get_actual_cxx_stdlib $binary_files] - puts $fd "@cxx_stdlib ${actual_cxx_stdlib}" - if {${actual_cxx_stdlib} ne "none"} { - variable cxx_stdlib_overridden [expr {${configure.cxx_stdlib} ne $cxx_stdlib}] - } else { - variable cxx_stdlib_overridden 0 - } - puts $fd "@cxx_stdlib_overridden ${cxx_stdlib_overridden}" - close $fd - - # Now create the archive - ui_debug "Creating [file tail $location]" - command_exec archive - ui_debug "Port image [file tail $location] created" - - # Cleanup all control files when finished - set control_files [glob -nocomplain -types f [file join $destpath +*]] - foreach file $control_files { - ui_debug "removing file: $file" - file delete -force $file - } -} - -proc portinstall::install_main {args} { - global subport version portpath depends_run revision user_options \ - portvariants requested_variants depends_lib PortInfo epoch \ - portarchivetype portimage_mode - variable file_is_binary - variable actual_cxx_stdlib - variable cxx_stdlib_overridden - variable installPlist - - set location [get_portimage_path] - set archive_path [find_portarchive_path 0] - if {$archive_path ne ""} { - set install_dir [file dirname $location] - file mkdir $install_dir - file rename -force $archive_path $install_dir - # Clean up statefile so the state is consistent now that the - # archive is moved, in case anything fails from here on. - delete [file join [option workpath] .macports.${subport}.state] - set location [file join $install_dir [file tail $archive_path]] - set current_archive_type [string range [file extension $location] 1 end] - set archive_metadata [extract_archive_metadata $location $current_archive_type {contents cxx_info}] - lassign [dict get $archive_metadata contents] installPlist file_is_binary - lassign [dict get $archive_metadata cxx_info] actual_cxx_stdlib cxx_stdlib_overridden - } else { - if {$portimage_mode eq "directory"} { - # Special value to avoid writing archive out to disk, since - # only the extracted dir should be kept. - set archivetype tmptar - set location [file rootname $location] - } else { - # throws an error if an unsupported value has been configured - archiveTypeIsSupported $portarchivetype - set archivetype $portarchivetype - } - # create archive from the destroot - create_archive $location $archivetype - } - - # can't do this inside the write transaction due to deadlock issues with _get_dep_port - set dep_portnames [list] - foreach deplist [list depends_lib depends_run] { - if {[info exists $deplist]} { - foreach dep [set $deplist] { - set dep_portname [_get_dep_port $dep] - if {$dep_portname ne ""} { - lappend dep_portnames $dep_portname - } - } - } - } - - set regref [dict create] - dict set regref name $subport - dict set regref version $version - dict set regref revision $revision - dict set regref variants $portvariants - dict set regref epoch $epoch - if {[info exists user_options(ports_requested)]} { - dict set regref requested $user_options(ports_requested) - } else { - dict set regref requested 0 - } - lassign [_get_compatible_platform] os_platform os_major - dict set regref os_platform $os_platform - dict set regref os_major $os_major - dict set regref archs [get_canonical_archs] - if {${actual_cxx_stdlib} ne ""} { - dict set regref cxx_stdlib ${actual_cxx_stdlib} - dict set regref cxx_stdlib_overridden ${cxx_stdlib_overridden} - } else { - # no info in the archive - global configure.cxx_stdlib cxx_stdlib - dict set regref cxx_stdlib_overridden [expr {${configure.cxx_stdlib} ne $cxx_stdlib}] - } - # Trick to have a portable GMT-POSIX epoch-based time. - dict set regref date [expr {[clock scan now -gmt true] - [clock scan "1970-1-1 00:00:00" -gmt true]}] - if {[info exists requested_variants]} { - dict set regref requested_variants $requested_variants - } - - dict set regref depends $dep_portnames - - dict set regref location $location - - if {[info exists installPlist]} { - dict set regref files $installPlist - dict set regref binary $file_is_binary - } - - # portfile info - dict set regref portfile_path [file join $portpath Portfile] - - # portgroup info - if {[info exists PortInfo(portgroups)]} { - set regref_pgs [list] - foreach pg $PortInfo(portgroups) { - lassign $pg pgname pgversion groupFile - if {[file isfile $groupFile]} { - lappend regref_pgs $pgname $pgversion $groupFile - } else { - ui_debug "install_main: no portgroup ${pgname}-${pgversion}.tcl found" - } - } - dict set regref portgroups $regref_pgs - } - - registry_install $regref - - return 0 -} diff --git a/src/port1.0/portinstall_run.tcl b/src/port1.0/portinstall_run.tcl new file mode 100644 index 000000000..02c58839d --- /dev/null +++ b/src/port1.0/portinstall_run.tcl @@ -0,0 +1,375 @@ +# -*- 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 +# +# Copyright (c) 2002 - 2004 Apple Inc. +# Copyright (c) 2004 Robert Shaw +# Copyright (c) 2005, 2007 - 2012 The MacPorts Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Apple Inc. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +package provide portinstall_run 1.0 + +namespace eval portinstall { + +# If the given path is in a git checkout, return the currently checked +# out commit. If not, return an empty string. +proc get_path_commit {path} { + set result "" + if {![catch {findBinary git} git] && ![catch {file type $path} ftype]} { + if {$ftype ne "directory"} { + set path [file dirname $path] + } + # Recent git refuses to run if the current user doesn't own + # the checkout. + if {[getuid] == 0} { + macports_try -pass_signal { + set prev_euid [geteuid] + set prev_egid [getegid] + if {[geteuid] != 0} { + seteuid 0 + } + # Must change egid before dropping root euid. + setegid [name_to_gid [file attributes $path -group]] + seteuid [name_to_uid [file attributes $path -owner]] + } on error {err} { + ui_debug "get_path_commit: dropping privileges failed: $err" + } + } + if {[catch {exec -ignorestderr $git -C $path rev-parse HEAD 2> /dev/null} result]} { + ui_debug "get_path_commit: git rev-parse failed: $result" + set result "" + } + } + if {[info exists prev_euid]} { + seteuid 0 + if {[info exists prev_egid]} { + setegid $prev_egid + } + seteuid $prev_euid + } + return $result +} + +proc install_start {args} { + global UI_PREFIX subport version revision portvariants \ + prefix_frozen + ui_notice "$UI_PREFIX [format [msgcat::mc "Installing %s @%s_%s%s"] $subport $version $revision $portvariants]" + + if {![file writable $prefix_frozen] || ([getuid] == 0 && [geteuid] != 0)} { + # if install location is not writable, need root privileges to install + # Also elevate if started as root, since 'file writable' doesn't seem + # to take euid into account. + elevateToRoot "install" + } + + # create any users and groups needed by the port + handle_add_users +} + +proc create_archive {location archive.type} { + global workpath destpath portpath subport version revision portvariants \ + archive.dir epoch configure.cxx_stdlib cxx_stdlib PortInfo \ + depends_lib depends_run xcodeversion xcodecltversion use_xcode \ + os.subplatform os.version macos_version source_date_epoch + + portarchive::archive_command_setup ${location} ${archive.type} + set archive.dir ${destpath} + + set archive.fulldestpath [file dirname $location] + # Create archive destination path (if needed) + if {![file isdirectory ${archive.fulldestpath}]} { + file mkdir ${archive.fulldestpath} + } + + # Create (if no files) destroot for archiving + if {![file isdirectory ${destpath}]} { + return -code error "no destroot found at: ${destpath}" + } + + # Copy state file into destroot for archiving + # +STATE contains a copy of the MacPorts state information + set statefile [file join $workpath .macports.${subport}.state] + file copy -force $statefile [file join $destpath "+STATE"] + + # Copy Portfile into destroot for archiving + # +PORTFILE contains a copy of the MacPorts Portfile + set portfile [file join $portpath Portfile] + file copy -force $portfile [file join $destpath "+PORTFILE"] + + # Create some informational files that we don't really use just yet, + # but we may in the future in order to allow port installation from + # archives without a full "ports" tree of Portfiles. + # + # Note: These have been modeled after FreeBSD type package files to + # start. We can change them however we want for actual future use if + # needed. + # + # +COMMENT contains the port description + set fd [open [file join $destpath "+COMMENT"] w] + if {[exists description]} { + puts $fd "[option description]" + } + close $fd + # +DESC contains the port long_description and homepage + set fd [open [file join $destpath "+DESC"] w] + if {[exists long_description]} { + puts $fd "[option long_description]" + } + if {[exists homepage]} { + puts $fd "\nWWW: [option homepage]" + } + close $fd + # +CONTENTS contains the port version/name info and all installed + # files and checksums + set control [list] + set fd [open [file join $destpath "+CONTENTS"] w] + puts $fd "@name ${subport}-${version}_${revision}${portvariants}" + puts $fd "@portname ${subport}" + puts $fd "@portepoch ${epoch}" + puts $fd "@portversion ${version}" + puts $fd "@portrevision ${revision}" + puts $fd "@archs [get_canonical_archs]" + set ourvariations $PortInfo(active_variants) + set vlist [lsort -ascii [dict keys $ourvariations]] + foreach v $vlist { + if {[dict get $ourvariations $v] eq "+"} { + puts $fd "@portvariant +${v}" + } + } + + foreach key [list depends_lib depends_run] { + if {[info exists $key]} { + foreach depspec [set $key] { + set depname [lindex [split $depspec :] end] + set dep [mport_lookup $depname] + if {[llength $dep] < 2} { + ui_debug "Dependency $depname not found" + } else { + lassign $dep depname dep_portinfo + set depver [dict get $dep_portinfo version] + set deprev [dict get $dep_portinfo revision] + puts $fd "@pkgdep ${depname}-${depver}_${deprev}" + } + } + } + } + + puts $fd "@macports_version [macports_version]" + lassign [_get_compatible_platform] compat_platform compat_major + if {$compat_platform ne "any"} { + if {${os.subplatform} ne ""} { + puts $fd "@os.subplatform ${os.subplatform}" + } + if {$compat_major ne "any"} { + puts $fd "@os.version ${os.version}" + if {$macos_version ne ""} { + puts $fd "@macos_version $macos_version" + } + if {$use_xcode && $xcodeversion ni {"" none}} { + puts $fd "@xcodeversion $xcodeversion" + } elseif {$xcodecltversion ni {"" none}} { + puts $fd "@xcodecltversion $xcodecltversion" + } + } + } + set ports_commit [get_path_commit $portpath] + if {$ports_commit ne ""} { + puts $fd "@ports_commit $ports_commit" + } + puts $fd "@source_date_epoch $source_date_epoch" + + set have_fileIsBinary [expr {[option os.platform] eq "darwin"}] + set binary_files [list] + variable file_is_binary [dict create] + # also save the contents for our own use later + variable installPlist [list] + set destpathLen [string length $destpath] + fs-traverse -depth fullpath [list $destpath] { + if {[file type $fullpath] eq "directory"} { + continue + } + + set relpath [string range $fullpath $destpathLen+1 end] + if {[string index $relpath 0] ne "+"} { + puts $fd "$relpath" + set abspath [file join [file separator] $relpath] + lappend installPlist $abspath + if {[file isfile $fullpath]} { + ui_debug "checksum file: $fullpath" + set checksum [md5 file $fullpath] + puts $fd "@comment MD5:$checksum" + if {$have_fileIsBinary} { + # test if (mach-o) binary + set is_binary [fileIsBinary $fullpath] + if {$is_binary} { + lappend binary_files $fullpath + } + puts $fd "@comment binary:$is_binary" + dict set file_is_binary $abspath $is_binary + } + } + } else { + lappend control $relpath + } + } + foreach relpath $control { + puts $fd "@ignore" + puts $fd "$relpath" + } + variable actual_cxx_stdlib [get_actual_cxx_stdlib $binary_files] + puts $fd "@cxx_stdlib ${actual_cxx_stdlib}" + if {${actual_cxx_stdlib} ne "none"} { + variable cxx_stdlib_overridden [expr {${configure.cxx_stdlib} ne $cxx_stdlib}] + } else { + variable cxx_stdlib_overridden 0 + } + puts $fd "@cxx_stdlib_overridden ${cxx_stdlib_overridden}" + close $fd + + # Now create the archive + ui_debug "Creating [file tail $location]" + command_exec archive + ui_debug "Port image [file tail $location] created" + + # Cleanup all control files when finished + set control_files [glob -nocomplain -types f [file join $destpath +*]] + foreach file $control_files { + ui_debug "removing file: $file" + file delete -force $file + } +} + +proc install_main {args} { + global subport version portpath depends_run revision user_options \ + portvariants requested_variants depends_lib PortInfo epoch \ + portarchivetype portimage_mode + variable file_is_binary + variable actual_cxx_stdlib + variable cxx_stdlib_overridden + variable installPlist + + set location [get_portimage_path] + set archive_path [find_portarchive_path 0] + if {$archive_path ne ""} { + set install_dir [file dirname $location] + file mkdir $install_dir + file rename -force $archive_path $install_dir + # Clean up statefile so the state is consistent now that the + # archive is moved, in case anything fails from here on. + delete [file join [option workpath] .macports.${subport}.state] + set location [file join $install_dir [file tail $archive_path]] + set current_archive_type [string range [file extension $location] 1 end] + set archive_metadata [extract_archive_metadata $location $current_archive_type {contents cxx_info}] + lassign [dict get $archive_metadata contents] installPlist file_is_binary + lassign [dict get $archive_metadata cxx_info] actual_cxx_stdlib cxx_stdlib_overridden + } else { + if {$portimage_mode eq "directory"} { + # Special value to avoid writing archive out to disk, since + # only the extracted dir should be kept. + set archivetype tmptar + set location [file rootname $location] + } else { + # throws an error if an unsupported value has been configured + archiveTypeIsSupported $portarchivetype + set archivetype $portarchivetype + } + # create archive from the destroot + create_archive $location $archivetype + } + + # can't do this inside the write transaction due to deadlock issues with _get_dep_port + set dep_portnames [list] + foreach deplist [list depends_lib depends_run] { + if {[info exists $deplist]} { + foreach dep [set $deplist] { + set dep_portname [_get_dep_port $dep] + if {$dep_portname ne ""} { + lappend dep_portnames $dep_portname + } + } + } + } + + set regref [dict create] + dict set regref name $subport + dict set regref version $version + dict set regref revision $revision + dict set regref variants $portvariants + dict set regref epoch $epoch + if {[info exists user_options(ports_requested)]} { + dict set regref requested $user_options(ports_requested) + } else { + dict set regref requested 0 + } + lassign [_get_compatible_platform] os_platform os_major + dict set regref os_platform $os_platform + dict set regref os_major $os_major + dict set regref archs [get_canonical_archs] + if {${actual_cxx_stdlib} ne ""} { + dict set regref cxx_stdlib ${actual_cxx_stdlib} + dict set regref cxx_stdlib_overridden ${cxx_stdlib_overridden} + } else { + # no info in the archive + global configure.cxx_stdlib cxx_stdlib + dict set regref cxx_stdlib_overridden [expr {${configure.cxx_stdlib} ne $cxx_stdlib}] + } + # Trick to have a portable GMT-POSIX epoch-based time. + dict set regref date [expr {[clock scan now -gmt true] - [clock scan "1970-1-1 00:00:00" -gmt true]}] + if {[info exists requested_variants]} { + dict set regref requested_variants $requested_variants + } + + dict set regref depends $dep_portnames + + dict set regref location $location + + if {[info exists installPlist]} { + dict set regref files $installPlist + dict set regref binary $file_is_binary + } + + # portfile info + dict set regref portfile_path [file join $portpath Portfile] + + # portgroup info + if {[info exists PortInfo(portgroups)]} { + set regref_pgs [list] + foreach pg $PortInfo(portgroups) { + lassign $pg pgname pgversion groupFile + if {[file isfile $groupFile]} { + lappend regref_pgs $pgname $pgversion $groupFile + } else { + ui_debug "install_main: no portgroup ${pgname}-${pgversion}.tcl found" + } + } + dict set regref portgroups $regref_pgs + } + + registry_install $regref + + return 0 +} + +} diff --git a/src/port1.0/portlint.tcl b/src/port1.0/portlint.tcl index 1af24f709..49f2935f2 100644 --- a/src/port1.0/portlint.tcl +++ b/src/port1.0/portlint.tcl @@ -1,36 +1,8 @@ -# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4 -# -# Copyright (c) 2007 - 2018, 2020 The MacPorts Project -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of The MacPorts Project nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# +# -*- 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 + +# License: see portlint_run.tcl package provide portlint 1.0 -package require portutil 1.0 -package require portchecksum 1.0 set org.macports.lint [target_new org.macports.lint portlint::lint_main] target_runtype ${org.macports.lint} always @@ -38,879 +10,4 @@ target_state ${org.macports.lint} no target_provides ${org.macports.lint} lint target_requires ${org.macports.lint} main target_prerun ${org.macports.lint} portlint::lint_start - -namespace eval portlint { - variable lint_portsystem \ - "1.0" - - variable lint_platforms [list \ - "any" \ - "darwin" \ - "freebsd" \ - "linux" \ - "macosx" \ - "netbsd" \ - "openbsd" \ - "puredarwin" \ - "solaris" \ - "sunos" \ - ] - - variable lint_required [list \ - "name" \ - "version" \ - "description" \ - "long_description" \ - "categories" \ - "maintainers" \ - "platforms" \ - "homepage" \ - "master_sites" \ - "checksums" \ - "license" \ - ] - - variable lint_optional [list \ - "epoch" \ - "revision" \ - "worksrcdir" \ - "distname" \ - "use_automake" \ - "use_autoconf" \ - "use_autoreconf" \ - "use_configure" \ - ] -} - -# lint_checksum_types -# -# Given a list of checksum types, return a list of strings which are warnings -# about deprecated checksum types, or missing recommended types. -# -# Returns an empty list if no issues are found. -proc portlint::lint_checksum_type_list {types} { - set issues [list] - set using_secure false - - foreach preferred $::portchecksum::secure_checksum_types { - if {$preferred ni $types} { - lappend issues "missing recommended checksum type: $preferred" - } else { - set using_secure true - } - } - global ports_lint_nitpick - if {[tbool ports_lint_nitpick] && "size" ni $types} { - lappend issues "missing recommended checksum type: size" - } - - if {!$using_secure} { - foreach type $types { - lappend issues "checksum type is insecure on its own: $type" - } - } - - return $issues -} - -# verify_checksum_format -# -# Given a checksum type as string and the actual checksum: -# -# - return 1 if the value has the expected format -# - return 0 if the value does not look as expected -# - return -1 if the checksum type is unrecognized -proc portlint::verify_checksum_format {type value} { - switch [string tolower $type] { - sha256 - - blake3 { - return [regexp {^[0-9a-f]{64}$} $value] - } - rmd160 - - sha1 { - return [regexp {^[0-9a-f]{40}$} $value] - } - md5 { - return [regexp {^[0-9a-f]{32}$} $value] - } - size { - return [regexp {^\d+$} $value] - } - default { - # unrecognized checksum type - return -1 - } - } -} - -# lint_checksum -# -# Checks a given Portfile checksum string. Returns a list of lists. -# The first member list is a list of error strings. -# The second member list is a list of warning strings. -# -# Returns a list containing two empty lists if no issues are found. -proc portlint::lint_checksum {checksum_string} { - set errors [list] - set warnings [list] - - set is_error false - set ctr_start 0 - - set filename "" - set pfx "" - set has_filenames false - - set types [list] - - # List of all tokens in the checksum string - set checksum_tokens [regexp -all -inline {\S+} $checksum_string] - - if {[lindex $checksum_tokens 0] eq "checksum"} { - incr ctr_start - } - - for {set ctr $ctr_start} \ - {($ctr < [llength $checksum_tokens]) && !$is_error} \ - {} { - - set current [lindex $checksum_tokens $ctr] - - if {$current in $::portchecksum::checksum_types} { - set c_type $current - set c_value [lindex $checksum_tokens $ctr+1] - - switch [portlint::verify_checksum_format $c_type $c_value] { - 1 { - # checksum type recognized, and checksum looks good - incr ctr 2 - lappend types $c_type - } - - 0 { - # checksum type recognized, but checksum looks bad - lappend errors "${pfx}checksum type $c_type, but\ - checksum is invalid: $c_value" - incr ctr 2 - } - - -1 { - # checksum type not recognized - lappend errors "${pfx}invalid checksum type: $c_type\ - $c_value" - set is_error true - continue - } - } - - } elseif {($ctr > $ctr_start) && !$has_filenames} { - lappend errors "invalid checksum field: $current" - set is_error true - continue - } else { - if {$ctr == $ctr_start} { - set has_filenames true - } elseif {($ctr == ([llength $checksum_tokens] - 1)) || \ - ([portlint::verify_checksum_format \ - [lindex $checksum_tokens $ctr-2] \ - [lindex $checksum_tokens $ctr-1] \ - ] != 1)} { - lappend errors "invalid checksum field: $current" - set is_error true - continue - } - - if {[llength $types] > 0} { - set types_lint [portlint::lint_checksum_type_list $types] - foreach lint_issue $types_lint { - lappend warnings "${pfx}${lint_issue}" - } - } - - set filename $current - set pfx "$filename - " - set types [list] - incr ctr - } - } - - if {[llength $types] > 0} { - set types_lint [portlint::lint_checksum_type_list $types] - foreach lint_issue $types_lint { - lappend warnings "${pfx}${lint_issue}" - } - } - - return [list $errors $warnings] -} - -# lint_platforms -# -# Checks a given Portfile platforms string. Returns a list of lists. -# The first member list is a list of error strings. -# The second member list is a list of warning strings. -# -# Returns a list containing two empty lists if no issues are found. -proc portlint::lint_platforms {platforms} { - set errors [list] - set warnings [list] - - global name subport PortInfo - variable lint_platforms - - foreach platform $platforms { - set platname [lindex $platform 0] - if {$platname ni $lint_platforms} { - lappend errors "Unknown platform: $platname" - } - } - - # Skip if there are any subports in the Portfile, as lint is not smart - # enough to know which platforms line belongs to which subport. - if {$platforms eq "darwin" && [string equal -nocase $name $subport] - && (![info exists PortInfo(subports)] || $PortInfo(subports) eq {}) - } then { - lappend warnings "Unnecessary platforms line as darwin is the default" - } - - return [list $errors $warnings] -} - -proc portlint::lint_start {args} { - global UI_PREFIX subport - ui_notice "$UI_PREFIX [format [msgcat::mc "Verifying Portfile for %s"] ${subport}]" -} - -proc portlint::lint_main {args} { - global UI_PREFIX portpath ports_lint_nitpick - variable lint_required; variable lint_optional - global {*}$lint_required {*}$lint_optional - set portfile ${portpath}/Portfile - set portdirs [split ${portpath} /] - set last [llength $portdirs] - incr last -1 - set portdir [lindex $portdirs $last] - incr last -1 - set portcatdir [lindex $portdirs $last] - - set warnings 0 - set errors 0 - - ################################################################### - ui_debug "$portfile" - - if {[info exists ports_lint_nitpick] && $ports_lint_nitpick eq "yes"} { - set nitpick true - } else { - set nitpick false - } - - set topline_number 1 - set require_blank false - set require_after "" - set seen_portsystem false - set seen_portgroup false - set seen_platforms false - set in_description false - set prohibit_tabs false - - set portgroups [dict create] - - set local_variants [list] - - set f [open $portfile RDONLY] - set lineno 1 - set hashline false - while {1} { - set line [gets $f] - if {[eof $f]} { - seek $f -1 end - set last [read $f 1] - if {"\n" ne $last} { - ui_warn "Line $lineno has missing newline (at end of file)" - incr warnings - } - close $f - break - } - ui_debug "$lineno: $line" - - if {($require_after eq "PortSystem" || $require_after eq "PortGroup") && \ - [regexp {^\s*PortGroup\s} $line]} { - set require_blank false - } - - if {$nitpick && $require_blank && ($line ne "")} { - ui_warn "Line $lineno should be a newline (after $require_after)" - incr warnings - } - set require_blank false - - if {$nitpick && [regexp {\S[ \t]+$} $line]} { - # allow indented blank lines between blocks of code and such - ui_warn "Line $lineno has trailing whitespace before newline" - incr warnings - } - - if {($lineno == $topline_number) && [string match "*-\*- *" $line]} { - ui_info "OK: Line $lineno has emacs/vim Mode" - incr topline_number - set require_blank true - set require_after "modeline" - if {[regexp {\sindent-tabs-mode: nil[;\s]|[:\s](?:et|expandtab)(?:[:\s]|$)} $line]} { - set prohibit_tabs true - } - } - - if {$prohibit_tabs && [string match "*\t*" $line]} { - ui_warn "Line $lineno contains tab but modeline says tabs should be expanded" - incr warnings - } - - if {[string match "*\$Id*\$" $line]} { - ui_warn "Line $lineno is using obsolete RCS tag (\$Id\$)" - incr warnings - } - - # skip the rest for comment lines (not perfectly accurate...) - if {[regexp {^\s*#} $line]} { - incr lineno - continue - } - - if {[regexp {^\s*PortSystem\s} $line]} { - if {$seen_portsystem} { - ui_error "Line $lineno repeats PortSystem declaration" - incr errors - } - regexp {^\s*PortSystem\s+([0-9.]+)\s*$} $line -> portsystem - if {![info exists portsystem]} { - ui_error "Line $lineno has unrecognized PortSystem" - incr errors - } - set seen_portsystem true - set require_blank true - set require_after "PortSystem" - } - if {[regexp {^\s*PortGroup\s} $line]} { - regexp {^\s*PortGroup\s+([A-Za-z0-9_]+)\s+([0-9.]+)\s*$} $line -> portgroup portgroupversion - if {![info exists portgroup]} { - ui_error "Line $lineno has unrecognized PortGroup" - incr errors - } else { - if {[dict exists $portgroups $portgroup]} { - ui_error "Line $lineno repeats inclusion of PortGroup $portgroup" - incr errors - } else { - dict set portgroups $portgroup $portgroupversion - } - } - set seen_portgroup true - set require_blank true - set require_after "PortGroup" - } - if {[regexp {^\s*platforms\s} $line]} { - set seen_platforms true - } - - # TODO: check for repeated variable definitions - # TODO: check the definition order of variables - # TODO: check length of description against max - - if {[regexp {^\s*long_description\s} $line]} { - set in_description true - } - if {$in_description && ([string range $line end end] ne "\\")} { - set in_description false - #set require_blank true - #set require_after "long_description" - } elseif {$in_description} { - set require_blank false - } - - if {[regexp {^\s*variant\s} $line]} { - regexp {^\s*variant\s+(\w+)} $line -> variantname - if {[info exists variantname]} { - lappend local_variants $variantname - } - } - - if {[regexp {^\s*platform\s} $line]} { - regexp {^\s*platform\s+(?:\w+\s+(?:\w+\s+)?)?(\w+)} $line -> platform_arch - foreach {bad_platform_arch replacement_platform_arch} { - arm64 arm - intel i386 - ppc powerpc - ppc64 powerpc - x86_64 i386 - } { - if {$platform_arch eq $bad_platform_arch} { - ui_error "Arch '$bad_platform_arch' in platform on line $lineno should be '$replacement_platform_arch'" - incr errors - } - } - } - - if {[regexp {^\s*adduser\s} $line]} { - ui_warn "Line $lineno calling adduser directly; consider setting add_users instead" - incr warnings - } - - if {[regexp {^\s*configure\s+\{\s*\}} $line]} { - ui_warn "Line $lineno should say \"use_configure no\" instead of declaring an empty configure phase" - incr warnings - } - - if {[regexp {(^.*)(\meval\s+)(.*)(\[glob\M)(.*$)} $line -> match_before match_eval match_between match_glob match_after]} { - ui_warn "Line $lineno should use the expansion operator instead of the eval procedure. Change" - ui_warn "$line" - ui_warn "to" - ui_warn "$match_before$match_between{*}$match_glob$match_after" - incr warnings - } - - # Check for hardcoded version numbers - if {$nitpick} { - # Support for skipping checksums lines - if {[regexp {^\s*checksums\s} $line]} { - # We enter a series of one or more lines containing checksums - set hashline true - } - - if {!$hashline - && ![regexp {^\s*(?:PortSystem|PortGroup|version|python\.versions|(?:perl5|php|ruby)\.branch(?:es)?|license|[A-Za-z0-9_]+\.setup)\s} $line] - && [string first $version $line] != -1} { - ui_warn "Line $lineno seems to hardcode the version number, consider using \${version} instead" - incr warnings - } - - if {$hashline && - ![string match \\\\ [string index $line end]]} { - # if the last character is not a backslash we're done with - # line skipping - set hashline false - } - } - - # Check for hardcoded paths - if {!$hashline - && $name ne "MacPorts" - && [string match "*/opt/local*" $line] - && ![regexp {^\s*(?:reinplace\s|system.*\Wsed\W)} $line]} { - ui_error "Line $lineno hardcodes /opt/local, use \${prefix} instead" - incr errors - } - - if {[regexp {\$\{?macosx_version} $line]} { - ui_warn "Line $lineno using macosx_version; switch to macos_version or macos_version_major" - incr warnings - } - - ### TODO: more checks to Portfile syntax - - incr lineno - } - - ################################################################### - - global porturl portutil::all_variants patchfiles \ - depends_fetch depends_extract depends_patch \ - depends_lib depends_build depends_run \ - depends_test distfiles fetch.type \ - replaced_by conflicts - variable lint_portsystem - set portarch [get_canonical_archs] - - if {!$seen_portsystem} { - ui_error "Didn't find PortSystem specification" - incr errors - } elseif {$portsystem ne $lint_portsystem} { - ui_error "Unknown PortSystem: $portsystem" - incr errors - } else { - ui_info "OK: Found PortSystem $portsystem" - } - - if {$seen_portgroup} { - # Using a PortGroup is optional - dict for {portgroup portgroupversion} $portgroups { - if {![file exists [getportresourcepath $porturl "port1.0/group/${portgroup}-${portgroupversion}.tcl"]]} { - ui_error "Unknown PortGroup: $portgroup-$portgroupversion" - incr errors - } else { - ui_info "OK: Found PortGroup $portgroup-$portgroupversion" - } - } - } - - foreach req_var $lint_required { - if {$req_var eq "master_sites"} { - if {${fetch.type} ne "standard"} { - ui_info "OK: $req_var not required for fetch.type ${fetch.type}" - continue - } - if {[llength ${distfiles}] == 0} { - ui_info "OK: $req_var not required when there are no distfiles" - continue - } - } - - if {![info exists $req_var]} { - ui_error "Missing required variable: $req_var" - incr errors - } else { - ui_info "OK: Found required variable: $req_var" - } - } - - foreach opt_var $lint_optional { - if {[info exists $opt_var]} { - # TODO: check whether it was seen (or default) - ui_info "OK: Found optional variable: $opt_var" - } - } - - if {[info exists name]} { - if {[regexp {[^[:alnum:]_.-]} $name]} { - ui_error "Port name '$name' contains unsafe characters. Names should only contain alphanumeric characters, underscores, dashes or dots." - incr errors - } - } - - if {[info exists version] && [string match *+* $version]} { - ui_warn "Version '$version' contains '+' character, which makes it difficult to specify on the command line due to conflict with variant syntax." - incr warnings - } - - if {$seen_platforms && [info exists platforms]} { - set platforms_lint [portlint::lint_platforms $platforms] - - foreach err [lindex $platforms_lint 0] { - ui_error $err - incr errors - } - - foreach warning [lindex $platforms_lint 1] { - ui_warn $warning - incr warnings - } - } - - if {[info exists categories]} { - if {[llength $categories] > 0} { - set category [lindex $categories 0] - ui_info "OK: Found primary category: $category" - } else { - ui_error "Categories list is empty" - incr errors - } - } - - set variantnumber 1 - foreach variant $all_variants { - set variantname [ditem_key $variant name] - set variantdesc [lindex [ditem_key $variant description] 0] - if {![info exists variantname] || $variantname eq ""} { - ui_error "Variant number $variantnumber does not have a name" - incr errors - } else { - set name_ok true - set desc_ok true - - if {![regexp {^[A-Za-z0-9_.]+$} $variantname]} { - ui_error "Variant name $variantname is not valid; use \[A-Za-z0-9_.\]+ only" - incr errors - set name_ok false - } - - global portresourcepath - if {![info exists portresourcepath]} { - global porturl - set portresourcepath [getportresourcepath $porturl] - } - - if {![info exists variantdesc] || $variantdesc eq ""} { - # don't warn about missing descriptions for global variants - if {$variantname in $local_variants && - [get_variant_description $variantname $portresourcepath] eq ""} { - ui_warn "Variant $variantname does not have a description" - incr warnings - set desc_ok false - } elseif {$variantdesc eq ""} { - set variantdesc "(pre-defined variant)" - } - } elseif {[get_variant_description $variantname $portresourcepath] ne ""} { - ui_warn "Variant $variantname overrides global description" - incr warnings - } - - # Check if conflicting variants actually exist - foreach vconflict [ditem_key $variant conflicts] { - set exists 0 - foreach v $all_variants { - if {$vconflict eq [ditem_key $v name]} { - set exists 1 - break - } - } - if {!$exists} { - ui_warn "Variant $variantname conflicts with non-existing variant $vconflict" - incr warnings - } - } - - if {$name_ok} { - if {$desc_ok} { - ui_info "OK: Found variant $variantname: $variantdesc" - } else { - ui_info "OK: Found variant: $variantname" - } - } - } - incr variantnumber - } - - set all_depends [list] - if {[info exists depends_fetch]} { - lappend all_depends {*}$depends_fetch - } - if {[info exists depends_extract]} { - lappend all_depends {*}$depends_extract - } - if {[info exists depends_patch]} { - lappend all_depends {*}$depends_patch - } - if {[info exists depends_lib]} { - lappend all_depends {*}$depends_lib - } - if {[info exists depends_build]} { - lappend all_depends {*}$depends_build - } - if {[info exists depends_run]} { - lappend all_depends {*}$depends_run - } - if {[info exists depends_test]} { - lappend all_depends {*}$depends_test - } - foreach depspec $all_depends { - set dep [lindex [split $depspec :] end] - if {[catch {set res [mport_lookup $dep]} error]} { - ui_debug $::errorInfo - continue - } - if {$res eq ""} { - ui_error "Unknown dependency: $dep" - incr errors - } else { - ui_info "OK: Found dependency: $dep" - } - } - - # Check for multiple dependencies - foreach deptype {depends_extract depends_patch depends_lib depends_build depends_run depends_test} { - if {[info exists $deptype]} { - set depwarned [dict create] - foreach depspec [set $deptype] { - if {![dict exists $depwarned $depspec] - && [llength [lsearch -exact -all [set $deptype] $depspec]] > 1} { - ui_warn "Dependency $depspec specified multiple times in $deptype" - incr warnings - # Report each depspec only once - dict set depwarned $depspec yes - } - } - } - } - - if {[info exists replaced_by]} { - if {[regexp {[^[:alnum:]_.-]} $replaced_by]} { - ui_error "replaced_by should be a single port name, invalid value: $replaced_by" - incr errors - } else { - if {[catch {set res [mport_lookup $replaced_by]} error]} { - ui_debug $::errorInfo - } - if {$res eq ""} { - ui_error "replaced_by references unknown port: $replaced_by" - incr errors - } else { - ui_info "OK: replaced_by $replaced_by" - } - } - } - - if {[info exists checksums]} { - set checksum_lint [portlint::lint_checksum $checksums] - - foreach err [lindex $checksum_lint 0] { - ui_error $err - incr errors - } - - foreach warning [lindex $checksum_lint 1] { - ui_warn $warning - incr warnings - } - } - - if {[info exists conflicts]} { - foreach cport $conflicts { - if {[regexp {[^[:alnum:]_.-]} $cport]} { - ui_error "conflicts lists invalid value, should be port name: $cport" - incr errors - continue - } - if {[catch {set res [mport_lookup $cport]} error]} { - ui_debug $::errorInfo - continue - } - if {$res eq ""} { - ui_error "conflicts references unknown port: $cport" - incr errors - } else { - ui_info "OK: conflicts $cport" - } - } - } - - if {[regexp "^(.+)nomaintainer(@macports\.org)?(.+)$" $maintainers] } { - ui_error "Using nomaintainer together with other maintainer" - incr errors - } - - if {[regexp "^openmaintainer(@macports\.org)?$" $maintainers] } { - ui_error "Using openmaintainer without any other maintainer" - incr errors - } - - foreach maintainer $maintainers { - foreach addr $maintainer { - if {$addr eq "nomaintainer@macports.org" || - $addr eq "openmaintainer@macports.org"} { - ui_warn "Using full email address for no/open maintainer" - incr warnings - } elseif {[regexp "^(.+)@macports\.org$" $addr -> localpart]} { - ui_warn "Maintainer email address for $localpart includes @macports.org" - incr warnings - } elseif {$addr eq "darwinports@opendarwin\.org"} { - ui_warn "Using legacy email address for no/open maintainer" - incr warnings - } elseif {[regexp "^(.+)@(.+)$" $addr -> localpart domain]} { - ui_warn "Maintainer email address should be obfuscated as $domain:$localpart" - incr warnings - } - } - } - - if {$license eq "unknown"} { - ui_warn "no license set" - incr warnings - } else { - - # If maintainer set license, it must follow correct format - - set prev '' - foreach test [split [string map { \{ '' \} ''} $license] '\ '] { - ui_debug "Checking format of license '${test}'" - - # space instead of hyphen - if {[string is double -strict $test]} { - ui_error "Invalid license '${prev} ${test}': missing hyphen between ${prev} ${test}" - incr errors - - # missing hyphen - } elseif {![string equal -nocase "X11" $test]} { - foreach subtest [split $test '-'] { - ui_debug "testing ${subtest}" - - # license names start with letters: versions and empty strings need not apply - if {[string is alpha -strict [string index $subtest 0]]} { - - # if the last character of license name is a number or plus sign - # then a hyphen is missing - set license_end [string index $subtest end] - if {"+" eq $license_end || [string is digit -strict $license_end]} { - ui_error "invalid license '${test}': missing hyphen before version" - incr errors - } - } - } - } - - if {[string equal -nocase "BSD-2" $test]} { - # BSD-2 => BSD - ui_error "Invalid license '${test}': use BSD instead" - incr errors - } elseif {[string equal -nocase "BSD-3" $test]} { - # BSD-3 => BSD - ui_error "Invalid license '${test}': use BSD instead" - incr errors - } elseif {[string equal -nocase "BSD-4" $test]} { - # BSD-4 => BSD-old - ui_error "Invalid license '${test}': use BSD-old instead" - incr errors - } - - set prev $test - } - - } - - # these checks are only valid for ports stored in the regular tree directories - if {[info exists category] && $portcatdir ne $category} { - ui_error "Portfile parent directory $portcatdir does not match primary category $category" - incr errors - } else { - ui_info "OK: Portfile parent directory matches primary category" - } - if {$portdir ne $name} { - ui_error "Portfile directory $portdir does not match port name $name" - incr errors - } else { - ui_info "OK: Portfile directory matches port name" - } - - if {$nitpick && [info exists patchfiles]} { - foreach patchfile $patchfiles { - set ext [file extension $patchfile] - if {$ext in {.Z .gz .bz2 .xz}} { - set ext [file extension [file rootname $patchfile]] - } - if {$ext ni {.diff .patch} && [file exists ${portpath}/files/${patchfile}]} { - ui_warn "Patchfile $patchfile does not follow the source patch naming policy \"*.diff\" or \"*.patch\"" - incr warnings - } - } - } - - # Check for use of deprecated options - set deprecated_options_name [get_deprecated_options] - global $deprecated_options_name - foreach option [array names $deprecated_options_name] { - set newoption [lindex [set ${deprecated_options_name}($option)] 0] - set refcount [lindex [set ${deprecated_options_name}($option)] 1] - - if {$refcount > 0} { - if {$newoption ne ""} { - ui_warn "Using deprecated option '$option', superseded by '$newoption'" - } else { - ui_warn "Using deprecated option '$option'" - } - incr warnings - } - } - - ### TODO: more checks to Tcl variables/sections - - ui_debug "Name: $name" - ui_debug "Epoch: $epoch" - ui_debug "Version: $version" - ui_debug "Revision: $revision" - ui_debug "Archs: $portarch" - - ################################################################### - - ui_notice "$UI_PREFIX [format [msgcat::mc "%d errors and %d warnings found."] $errors $warnings]" - - return {$errors > 0} -} +target_runpkg ${org.macports.lint} portlint_run diff --git a/src/port1.0/portlint_run.tcl b/src/port1.0/portlint_run.tcl new file mode 100644 index 000000000..83e48d21c --- /dev/null +++ b/src/port1.0/portlint_run.tcl @@ -0,0 +1,909 @@ +# -*- 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 +# +# Copyright (c) 2007 - 2018, 2020 The MacPorts Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The MacPorts Project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +package provide portlint_run 1.0 +package require portchecksum 1.0 + +namespace eval portlint { + +variable lint_portsystem \ + "1.0" + +variable lint_platforms [list \ + "any" \ + "darwin" \ + "freebsd" \ + "linux" \ + "macosx" \ + "netbsd" \ + "openbsd" \ + "puredarwin" \ + "solaris" \ + "sunos" \ + ] + +variable lint_required [list \ + "name" \ + "version" \ + "description" \ + "long_description" \ + "categories" \ + "maintainers" \ + "platforms" \ + "homepage" \ + "master_sites" \ + "checksums" \ + "license" \ + ] + +variable lint_optional [list \ + "epoch" \ + "revision" \ + "worksrcdir" \ + "distname" \ + "use_automake" \ + "use_autoconf" \ + "use_autoreconf" \ + "use_configure" \ + ] + +# lint_checksum_types +# +# Given a list of checksum types, return a list of strings which are warnings +# about deprecated checksum types, or missing recommended types. +# +# Returns an empty list if no issues are found. +proc lint_checksum_type_list {types} { + set issues [list] + set using_secure false + + foreach preferred $::portchecksum::secure_checksum_types { + if {$preferred ni $types} { + lappend issues "missing recommended checksum type: $preferred" + } else { + set using_secure true + } + } + global ports_lint_nitpick + if {[tbool ports_lint_nitpick] && "size" ni $types} { + lappend issues "missing recommended checksum type: size" + } + + if {!$using_secure} { + foreach type $types { + lappend issues "checksum type is insecure on its own: $type" + } + } + + return $issues +} + +# verify_checksum_format +# +# Given a checksum type as string and the actual checksum: +# +# - return 1 if the value has the expected format +# - return 0 if the value does not look as expected +# - return -1 if the checksum type is unrecognized +proc verify_checksum_format {type value} { + switch [string tolower $type] { + sha256 - + blake3 { + return [regexp {^[0-9a-f]{64}$} $value] + } + rmd160 - + sha1 { + return [regexp {^[0-9a-f]{40}$} $value] + } + md5 { + return [regexp {^[0-9a-f]{32}$} $value] + } + size { + return [regexp {^\d+$} $value] + } + default { + # unrecognized checksum type + return -1 + } + } +} + +# lint_checksum +# +# Checks a given Portfile checksum string. Returns a list of lists. +# The first member list is a list of error strings. +# The second member list is a list of warning strings. +# +# Returns a list containing two empty lists if no issues are found. +proc lint_checksum {checksum_string} { + set errors [list] + set warnings [list] + + set is_error false + set ctr_start 0 + + set filename "" + set pfx "" + set has_filenames false + + set types [list] + + # List of all tokens in the checksum string + set checksum_tokens [regexp -all -inline {\S+} $checksum_string] + + if {[lindex $checksum_tokens 0] eq "checksum"} { + incr ctr_start + } + + for {set ctr $ctr_start} \ + {($ctr < [llength $checksum_tokens]) && !$is_error} \ + {} { + + set current [lindex $checksum_tokens $ctr] + + if {$current in $::portchecksum::checksum_types} { + set c_type $current + set c_value [lindex $checksum_tokens $ctr+1] + + switch [verify_checksum_format $c_type $c_value] { + 1 { + # checksum type recognized, and checksum looks good + incr ctr 2 + lappend types $c_type + } + + 0 { + # checksum type recognized, but checksum looks bad + lappend errors "${pfx}checksum type $c_type, but\ + checksum is invalid: $c_value" + incr ctr 2 + } + + -1 { + # checksum type not recognized + lappend errors "${pfx}invalid checksum type: $c_type\ + $c_value" + set is_error true + continue + } + } + + } elseif {($ctr > $ctr_start) && !$has_filenames} { + lappend errors "invalid checksum field: $current" + set is_error true + continue + } else { + if {$ctr == $ctr_start} { + set has_filenames true + } elseif {($ctr == ([llength $checksum_tokens] - 1)) || \ + ([verify_checksum_format \ + [lindex $checksum_tokens $ctr-2] \ + [lindex $checksum_tokens $ctr-1] \ + ] != 1)} { + lappend errors "invalid checksum field: $current" + set is_error true + continue + } + + if {[llength $types] > 0} { + set types_lint [lint_checksum_type_list $types] + foreach lint_issue $types_lint { + lappend warnings "${pfx}${lint_issue}" + } + } + + set filename $current + set pfx "$filename - " + set types [list] + incr ctr + } + } + + if {[llength $types] > 0} { + set types_lint [lint_checksum_type_list $types] + foreach lint_issue $types_lint { + lappend warnings "${pfx}${lint_issue}" + } + } + + return [list $errors $warnings] +} + +# lint_platforms +# +# Checks a given Portfile platforms string. Returns a list of lists. +# The first member list is a list of error strings. +# The second member list is a list of warning strings. +# +# Returns a list containing two empty lists if no issues are found. +proc lint_platforms {platforms} { + set errors [list] + set warnings [list] + + global name subport PortInfo + variable lint_platforms + + foreach platform $platforms { + set platname [lindex $platform 0] + if {$platname ni $lint_platforms} { + lappend errors "Unknown platform: $platname" + } + } + + # Skip if there are any subports in the Portfile, as lint is not smart + # enough to know which platforms line belongs to which subport. + if {$platforms eq "darwin" && [string equal -nocase $name $subport] + && (![info exists PortInfo(subports)] || $PortInfo(subports) eq {}) + } then { + lappend warnings "Unnecessary platforms line as darwin is the default" + } + + return [list $errors $warnings] +} + +proc lint_start {args} { + global UI_PREFIX subport + ui_notice "$UI_PREFIX [format [msgcat::mc "Verifying Portfile for %s"] ${subport}]" +} + +proc lint_main {args} { + global UI_PREFIX portpath ports_lint_nitpick + variable lint_required; variable lint_optional + global {*}$lint_required {*}$lint_optional + set portfile ${portpath}/Portfile + set portdirs [split ${portpath} /] + set last [llength $portdirs] + incr last -1 + set portdir [lindex $portdirs $last] + incr last -1 + set portcatdir [lindex $portdirs $last] + + set warnings 0 + set errors 0 + + ################################################################### + ui_debug "$portfile" + + if {[info exists ports_lint_nitpick] && $ports_lint_nitpick eq "yes"} { + set nitpick true + } else { + set nitpick false + } + + set topline_number 1 + set require_blank false + set require_after "" + set seen_portsystem false + set seen_portgroup false + set seen_platforms false + set in_description false + set prohibit_tabs false + + set portgroups [dict create] + + set local_variants [list] + + set f [open $portfile RDONLY] + set lineno 1 + set hashline false + while {1} { + set line [gets $f] + if {[eof $f]} { + seek $f -1 end + set last [read $f 1] + if {"\n" ne $last} { + ui_warn "Line $lineno has missing newline (at end of file)" + incr warnings + } + close $f + break + } + ui_debug "$lineno: $line" + + if {($require_after eq "PortSystem" || $require_after eq "PortGroup") && \ + [regexp {^\s*PortGroup\s} $line]} { + set require_blank false + } + + if {$nitpick && $require_blank && ($line ne "")} { + ui_warn "Line $lineno should be a newline (after $require_after)" + incr warnings + } + set require_blank false + + if {$nitpick && [regexp {\S[ \t]+$} $line]} { + # allow indented blank lines between blocks of code and such + ui_warn "Line $lineno has trailing whitespace before newline" + incr warnings + } + + if {($lineno == $topline_number) && [string match "*-\*- *" $line]} { + ui_info "OK: Line $lineno has emacs/vim Mode" + incr topline_number + set require_blank true + set require_after "modeline" + if {[regexp {\sindent-tabs-mode: nil[;\s]|[:\s](?:et|expandtab)(?:[:\s]|$)} $line]} { + set prohibit_tabs true + } + } + + if {$prohibit_tabs && [string match "*\t*" $line]} { + ui_warn "Line $lineno contains tab but modeline says tabs should be expanded" + incr warnings + } + + if {[string match "*\$Id*\$" $line]} { + ui_warn "Line $lineno is using obsolete RCS tag (\$Id\$)" + incr warnings + } + + # skip the rest for comment lines (not perfectly accurate...) + if {[regexp {^\s*#} $line]} { + incr lineno + continue + } + + if {[regexp {^\s*PortSystem\s} $line]} { + if {$seen_portsystem} { + ui_error "Line $lineno repeats PortSystem declaration" + incr errors + } + regexp {^\s*PortSystem\s+([0-9.]+)\s*$} $line -> portsystem + if {![info exists portsystem]} { + ui_error "Line $lineno has unrecognized PortSystem" + incr errors + } + set seen_portsystem true + set require_blank true + set require_after "PortSystem" + } + if {[regexp {^\s*PortGroup\s} $line]} { + regexp {^\s*PortGroup\s+([A-Za-z0-9_]+)\s+([0-9.]+)\s*$} $line -> portgroup portgroupversion + if {![info exists portgroup]} { + ui_error "Line $lineno has unrecognized PortGroup" + incr errors + } else { + if {[dict exists $portgroups $portgroup]} { + ui_error "Line $lineno repeats inclusion of PortGroup $portgroup" + incr errors + } else { + dict set portgroups $portgroup $portgroupversion + } + } + set seen_portgroup true + set require_blank true + set require_after "PortGroup" + } + if {[regexp {^\s*platforms\s} $line]} { + set seen_platforms true + } + + # TODO: check for repeated variable definitions + # TODO: check the definition order of variables + # TODO: check length of description against max + + if {[regexp {^\s*long_description\s} $line]} { + set in_description true + } + if {$in_description && ([string range $line end end] ne "\\")} { + set in_description false + #set require_blank true + #set require_after "long_description" + } elseif {$in_description} { + set require_blank false + } + + if {[regexp {^\s*variant\s} $line]} { + regexp {^\s*variant\s+(\w+)} $line -> variantname + if {[info exists variantname]} { + lappend local_variants $variantname + } + } + + if {[regexp {^\s*platform\s} $line]} { + regexp {^\s*platform\s+(?:\w+\s+(?:\w+\s+)?)?(\w+)} $line -> platform_arch + foreach {bad_platform_arch replacement_platform_arch} { + arm64 arm + intel i386 + ppc powerpc + ppc64 powerpc + x86_64 i386 + } { + if {$platform_arch eq $bad_platform_arch} { + ui_error "Arch '$bad_platform_arch' in platform on line $lineno should be '$replacement_platform_arch'" + incr errors + } + } + } + + if {[regexp {^\s*adduser\s} $line]} { + ui_warn "Line $lineno calling adduser directly; consider setting add_users instead" + incr warnings + } + + if {[regexp {^\s*configure\s+\{\s*\}} $line]} { + ui_warn "Line $lineno should say \"use_configure no\" instead of declaring an empty configure phase" + incr warnings + } + + if {[regexp {(^.*)(\meval\s+)(.*)(\[glob\M)(.*$)} $line -> match_before match_eval match_between match_glob match_after]} { + ui_warn "Line $lineno should use the expansion operator instead of the eval procedure. Change" + ui_warn "$line" + ui_warn "to" + ui_warn "$match_before$match_between{*}$match_glob$match_after" + incr warnings + } + + # Check for hardcoded version numbers + if {$nitpick} { + # Support for skipping checksums lines + if {[regexp {^\s*checksums\s} $line]} { + # We enter a series of one or more lines containing checksums + set hashline true + } + + if {!$hashline + && ![regexp {^\s*(?:PortSystem|PortGroup|version|python\.versions|(?:perl5|php|ruby)\.branch(?:es)?|license|[A-Za-z0-9_]+\.setup)\s} $line] + && [string first $version $line] != -1} { + ui_warn "Line $lineno seems to hardcode the version number, consider using \${version} instead" + incr warnings + } + + if {$hashline && + ![string match \\\\ [string index $line end]]} { + # if the last character is not a backslash we're done with + # line skipping + set hashline false + } + } + + # Check for hardcoded paths + if {!$hashline + && $name ne "MacPorts" + && [string match "*/opt/local*" $line] + && ![regexp {^\s*(?:reinplace\s|system.*\Wsed\W)} $line]} { + ui_error "Line $lineno hardcodes /opt/local, use \${prefix} instead" + incr errors + } + + if {[regexp {\$\{?macosx_version} $line]} { + ui_warn "Line $lineno using macosx_version; switch to macos_version or macos_version_major" + incr warnings + } + + ### TODO: more checks to Portfile syntax + + incr lineno + } + + ################################################################### + + global porturl portutil::all_variants patchfiles \ + depends_fetch depends_extract depends_patch \ + depends_lib depends_build depends_run \ + depends_test distfiles fetch.type \ + replaced_by conflicts + variable lint_portsystem + set portarch [get_canonical_archs] + + if {!$seen_portsystem} { + ui_error "Didn't find PortSystem specification" + incr errors + } elseif {$portsystem ne $lint_portsystem} { + ui_error "Unknown PortSystem: $portsystem" + incr errors + } else { + ui_info "OK: Found PortSystem $portsystem" + } + + if {$seen_portgroup} { + # Using a PortGroup is optional + dict for {portgroup portgroupversion} $portgroups { + if {![file exists [getportresourcepath $porturl "port1.0/group/${portgroup}-${portgroupversion}.tcl"]]} { + ui_error "Unknown PortGroup: $portgroup-$portgroupversion" + incr errors + } else { + ui_info "OK: Found PortGroup $portgroup-$portgroupversion" + } + } + } + + foreach req_var $lint_required { + if {$req_var eq "master_sites"} { + if {${fetch.type} ne "standard"} { + ui_info "OK: $req_var not required for fetch.type ${fetch.type}" + continue + } + if {[llength ${distfiles}] == 0} { + ui_info "OK: $req_var not required when there are no distfiles" + continue + } + } + + if {![info exists $req_var]} { + ui_error "Missing required variable: $req_var" + incr errors + } else { + ui_info "OK: Found required variable: $req_var" + } + } + + foreach opt_var $lint_optional { + if {[info exists $opt_var]} { + # TODO: check whether it was seen (or default) + ui_info "OK: Found optional variable: $opt_var" + } + } + + if {[info exists name]} { + if {[regexp {[^[:alnum:]_.-]} $name]} { + ui_error "Port name '$name' contains unsafe characters. Names should only contain alphanumeric characters, underscores, dashes or dots." + incr errors + } + } + + if {[info exists version] && [string match *+* $version]} { + ui_warn "Version '$version' contains '+' character, which makes it difficult to specify on the command line due to conflict with variant syntax." + incr warnings + } + + if {$seen_platforms && [info exists platforms]} { + set platforms_lint [lint_platforms $platforms] + + foreach err [lindex $platforms_lint 0] { + ui_error $err + incr errors + } + + foreach warning [lindex $platforms_lint 1] { + ui_warn $warning + incr warnings + } + } + + if {[info exists categories]} { + if {[llength $categories] > 0} { + set category [lindex $categories 0] + ui_info "OK: Found primary category: $category" + } else { + ui_error "Categories list is empty" + incr errors + } + } + + set variantnumber 1 + foreach variant $all_variants { + set variantname [ditem_key $variant name] + set variantdesc [lindex [ditem_key $variant description] 0] + if {![info exists variantname] || $variantname eq ""} { + ui_error "Variant number $variantnumber does not have a name" + incr errors + } else { + set name_ok true + set desc_ok true + + if {![regexp {^[A-Za-z0-9_.]+$} $variantname]} { + ui_error "Variant name $variantname is not valid; use \[A-Za-z0-9_.\]+ only" + incr errors + set name_ok false + } + + global portresourcepath + if {![info exists portresourcepath]} { + global porturl + set portresourcepath [getportresourcepath $porturl] + } + + if {![info exists variantdesc] || $variantdesc eq ""} { + # don't warn about missing descriptions for global variants + if {$variantname in $local_variants && + [get_variant_description $variantname $portresourcepath] eq ""} { + ui_warn "Variant $variantname does not have a description" + incr warnings + set desc_ok false + } elseif {$variantdesc eq ""} { + set variantdesc "(pre-defined variant)" + } + } elseif {[get_variant_description $variantname $portresourcepath] ne ""} { + ui_warn "Variant $variantname overrides global description" + incr warnings + } + + # Check if conflicting variants actually exist + foreach vconflict [ditem_key $variant conflicts] { + set exists 0 + foreach v $all_variants { + if {$vconflict eq [ditem_key $v name]} { + set exists 1 + break + } + } + if {!$exists} { + ui_warn "Variant $variantname conflicts with non-existing variant $vconflict" + incr warnings + } + } + + if {$name_ok} { + if {$desc_ok} { + ui_info "OK: Found variant $variantname: $variantdesc" + } else { + ui_info "OK: Found variant: $variantname" + } + } + } + incr variantnumber + } + + set all_depends [list] + if {[info exists depends_fetch]} { + lappend all_depends {*}$depends_fetch + } + if {[info exists depends_extract]} { + lappend all_depends {*}$depends_extract + } + if {[info exists depends_patch]} { + lappend all_depends {*}$depends_patch + } + if {[info exists depends_lib]} { + lappend all_depends {*}$depends_lib + } + if {[info exists depends_build]} { + lappend all_depends {*}$depends_build + } + if {[info exists depends_run]} { + lappend all_depends {*}$depends_run + } + if {[info exists depends_test]} { + lappend all_depends {*}$depends_test + } + foreach depspec $all_depends { + set dep [lindex [split $depspec :] end] + if {[catch {set res [mport_lookup $dep]} error]} { + ui_debug $::errorInfo + continue + } + if {$res eq ""} { + ui_error "Unknown dependency: $dep" + incr errors + } else { + ui_info "OK: Found dependency: $dep" + } + } + + # Check for multiple dependencies + foreach deptype {depends_extract depends_patch depends_lib depends_build depends_run depends_test} { + if {[info exists $deptype]} { + set depwarned [dict create] + foreach depspec [set $deptype] { + if {![dict exists $depwarned $depspec] + && [llength [lsearch -exact -all [set $deptype] $depspec]] > 1} { + ui_warn "Dependency $depspec specified multiple times in $deptype" + incr warnings + # Report each depspec only once + dict set depwarned $depspec yes + } + } + } + } + + if {[info exists replaced_by]} { + if {[regexp {[^[:alnum:]_.-]} $replaced_by]} { + ui_error "replaced_by should be a single port name, invalid value: $replaced_by" + incr errors + } else { + if {[catch {set res [mport_lookup $replaced_by]} error]} { + ui_debug $::errorInfo + } + if {$res eq ""} { + ui_error "replaced_by references unknown port: $replaced_by" + incr errors + } else { + ui_info "OK: replaced_by $replaced_by" + } + } + } + + if {[info exists checksums]} { + set checksum_lint [lint_checksum $checksums] + + foreach err [lindex $checksum_lint 0] { + ui_error $err + incr errors + } + + foreach warning [lindex $checksum_lint 1] { + ui_warn $warning + incr warnings + } + } + + if {[info exists conflicts]} { + foreach cport $conflicts { + if {[regexp {[^[:alnum:]_.-]} $cport]} { + ui_error "conflicts lists invalid value, should be port name: $cport" + incr errors + continue + } + if {[catch {set res [mport_lookup $cport]} error]} { + ui_debug $::errorInfo + continue + } + if {$res eq ""} { + ui_error "conflicts references unknown port: $cport" + incr errors + } else { + ui_info "OK: conflicts $cport" + } + } + } + + if {[regexp "^(.+)nomaintainer(@macports\.org)?(.+)$" $maintainers] } { + ui_error "Using nomaintainer together with other maintainer" + incr errors + } + + if {[regexp "^openmaintainer(@macports\.org)?$" $maintainers] } { + ui_error "Using openmaintainer without any other maintainer" + incr errors + } + + foreach maintainer $maintainers { + foreach addr $maintainer { + if {$addr eq "nomaintainer@macports.org" || + $addr eq "openmaintainer@macports.org"} { + ui_warn "Using full email address for no/open maintainer" + incr warnings + } elseif {[regexp "^(.+)@macports\.org$" $addr -> localpart]} { + ui_warn "Maintainer email address for $localpart includes @macports.org" + incr warnings + } elseif {$addr eq "darwinports@opendarwin\.org"} { + ui_warn "Using legacy email address for no/open maintainer" + incr warnings + } elseif {[regexp "^(.+)@(.+)$" $addr -> localpart domain]} { + ui_warn "Maintainer email address should be obfuscated as $domain:$localpart" + incr warnings + } + } + } + + if {$license eq "unknown"} { + ui_warn "no license set" + incr warnings + } else { + + # If maintainer set license, it must follow correct format + + set prev '' + foreach test [split [string map { \{ '' \} ''} $license] '\ '] { + ui_debug "Checking format of license '${test}'" + + # space instead of hyphen + if {[string is double -strict $test]} { + ui_error "Invalid license '${prev} ${test}': missing hyphen between ${prev} ${test}" + incr errors + + # missing hyphen + } elseif {![string equal -nocase "X11" $test]} { + foreach subtest [split $test '-'] { + ui_debug "testing ${subtest}" + + # license names start with letters: versions and empty strings need not apply + if {[string is alpha -strict [string index $subtest 0]]} { + + # if the last character of license name is a number or plus sign + # then a hyphen is missing + set license_end [string index $subtest end] + if {"+" eq $license_end || [string is digit -strict $license_end]} { + ui_error "invalid license '${test}': missing hyphen before version" + incr errors + } + } + } + } + + if {[string equal -nocase "BSD-2" $test]} { + # BSD-2 => BSD + ui_error "Invalid license '${test}': use BSD instead" + incr errors + } elseif {[string equal -nocase "BSD-3" $test]} { + # BSD-3 => BSD + ui_error "Invalid license '${test}': use BSD instead" + incr errors + } elseif {[string equal -nocase "BSD-4" $test]} { + # BSD-4 => BSD-old + ui_error "Invalid license '${test}': use BSD-old instead" + incr errors + } + + set prev $test + } + + } + + # these checks are only valid for ports stored in the regular tree directories + if {[info exists category] && $portcatdir ne $category} { + ui_error "Portfile parent directory $portcatdir does not match primary category $category" + incr errors + } else { + ui_info "OK: Portfile parent directory matches primary category" + } + if {$portdir ne $name} { + ui_error "Portfile directory $portdir does not match port name $name" + incr errors + } else { + ui_info "OK: Portfile directory matches port name" + } + + if {$nitpick && [info exists patchfiles]} { + foreach patchfile $patchfiles { + set ext [file extension $patchfile] + if {$ext in {.Z .gz .bz2 .xz}} { + set ext [file extension [file rootname $patchfile]] + } + if {$ext ni {.diff .patch} && [file exists ${portpath}/files/${patchfile}]} { + ui_warn "Patchfile $patchfile does not follow the source patch naming policy \"*.diff\" or \"*.patch\"" + incr warnings + } + } + } + + # Check for use of deprecated options + set deprecated_options_name [get_deprecated_options] + global $deprecated_options_name + foreach option [array names $deprecated_options_name] { + set newoption [lindex [set ${deprecated_options_name}($option)] 0] + set refcount [lindex [set ${deprecated_options_name}($option)] 1] + + if {$refcount > 0} { + if {$newoption ne ""} { + ui_warn "Using deprecated option '$option', superseded by '$newoption'" + } else { + ui_warn "Using deprecated option '$option'" + } + incr warnings + } + } + + ### TODO: more checks to Tcl variables/sections + + ui_debug "Name: $name" + ui_debug "Epoch: $epoch" + ui_debug "Version: $version" + ui_debug "Revision: $revision" + ui_debug "Archs: $portarch" + + ################################################################### + + ui_notice "$UI_PREFIX [format [msgcat::mc "%d errors and %d warnings found."] $errors $warnings]" + + return {$errors > 0} +} + +} diff --git a/src/port1.0/portlivecheck.tcl b/src/port1.0/portlivecheck.tcl index 13ad7a125..c404c3dc4 100644 --- a/src/port1.0/portlivecheck.tcl +++ b/src/port1.0/portlivecheck.tcl @@ -1,51 +1,21 @@ -# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4 -# portlivecheck.tcl -# -# Copyright (c) 2007-2011, 2014, 2016 The MacPorts Project -# Copyright (c) 2005-2007 Paul Guyot , -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of The MacPorts Project nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# +# -*- 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 + +# License: see portlivecheck_run.tcl package provide portlivecheck 1.0 -package require portutil 1.0 -package require portfetch 1.0 set org.macports.livecheck [target_new org.macports.livecheck portlivecheck::livecheck_main] target_runtype ${org.macports.livecheck} always target_state ${org.macports.livecheck} no target_provides ${org.macports.livecheck} livecheck target_requires ${org.macports.livecheck} main - -namespace eval portlivecheck { -} +target_runpkg ${org.macports.livecheck} portlivecheck_run # define options -options livecheck.url livecheck.type livecheck.md5 livecheck.regex livecheck.branch livecheck.name livecheck.distname livecheck.version livecheck.ignore_sslcert livecheck.compression livecheck.curloptions livecheck.user_agent +options livecheck.url livecheck.type livecheck.md5 livecheck.regex \ + livecheck.branch livecheck.name livecheck.distname livecheck.version \ + livecheck.ignore_sslcert livecheck.compression livecheck.curloptions \ + livecheck.user_agent # defaults default livecheck.url {$homepage} @@ -61,307 +31,23 @@ default livecheck.compression yes default livecheck.curloptions [list --append-http-header "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"] default livecheck.user_agent {} -proc portlivecheck::livecheck_async_start {} { - _livecheck_main yes -} - -proc portlivecheck::_async_cleanup {} { - variable async_job - if {[info exists async_job]} { - curlwrap_async_cancel $async_job - unset async_job +namespace eval portlivecheck { + proc livecheck_async_start {} { + global org.macports.livecheck + portutil::target_load ${org.macports.livecheck} + _livecheck_main yes } - variable tempfilename - if {[info exists tempfilename]} { - file delete $tempfilename - unset tempfilename - } -} -proc portlivecheck::_livecheck_main {{async no}} { - global livecheck.url livecheck.type livecheck.md5 livecheck.regex livecheck.branch livecheck.name livecheck.distname livecheck.version \ - livecheck.ignore_sslcert \ - livecheck.compression \ - livecheck.curloptions \ - livecheck.user_agent \ - git.cmd \ - homepage portpath \ - master_sites name subport - - if {${livecheck.type} eq "none"} { - if {!$async} { - ui_debug "livecheck.type is none" + proc _async_cleanup {} { + variable async_job + if {[info exists async_job]} { + curlwrap_async_cancel $async_job + unset async_job } - return 0 - } - - variable async_job - variable tempfilename - - if {[info exists async_job]} { - if {$async} { - # Async fetch already started - return 0 - } - # Fetch was started asynchronously, wait for job to finish - # Loop with a reasonable timeout so we don't wait too long - # to handle events like signals. - while {![curlwrap_async_is_complete $async_job 500]} {} - lassign [curlwrap_async_result $async_job] status result - unset async_job - if {$status != 0} { - ui_error "cannot check if $subport was updated ($result)" + variable tempfilename + if {[info exists tempfilename]} { file delete $tempfilename unset tempfilename - return 0 - } - set async_done 1 - } else { - set async_done 0 - } - - set updated 0 - set updated_version "unknown" - set has_master_sites [info exists master_sites] - set has_homepage [info exists homepage] - if {!$has_homepage} { - set livecheck.url {} - } - - if {!$async} { - ui_debug "Port (livecheck) version is ${livecheck.version}" - } - - set curl_options ${livecheck.curloptions} - if {[tbool livecheck.ignore_sslcert]} { - lappend curl_options "--ignore-ssl-cert" - } - if {[tbool livecheck.compression]} { - lappend curl_options "--enable-compression" - } - if {${livecheck.user_agent} ne ""} { - lappend curl_options "--user-agent" - lappend curl_options "${livecheck.user_agent}" - } - - # Check _resources/port1.0/livecheck for available types. - set types_dir [getdefaultportresourcepath "port1.0/livecheck"] - if {[catch {set available_types [glob -directory $types_dir -tails -types f *.tcl]} result]} { - return -code 1 "No available types were found. Check '$types_dir'." - } - - # Convert available_types from a list of files (e.g., { freecode.tcl - # gnu.tcl ... }) into a string in the format "type|type|..." (e.g., - # "freecode|gnu|..."). - set available_types [regsub -all {\.tcl} [join $available_types |] {}] - - if {${livecheck.type} eq "default"} { - # Determine the default type from the mirror. - if {$has_master_sites} { - foreach {master_site} ${master_sites} { - if {[regexp "^($available_types)(?::(\[^:\]+))?" ${master_site} _ site subdir]} { - set subdirs [split $subdir /] - if {[llength $subdirs] > 1} { - if {[lindex $subdirs 0] eq "project"} { - set subdir [lindex $subdirs 1] - } else { - set subdir "" - } - } - if {${subdir} ne "" && ${livecheck.name} eq "default"} { - set livecheck.name ${subdir} - } - set livecheck.type ${site} - - break - } - } - } - # If the default type cannot be determined from the mirror, use the - # fallback. - if {${livecheck.type} eq "default"} { - set livecheck.type "fallback" - } - if {$has_homepage} { - if {[regexp {^http://code.google.com/p/([^/]+)} $homepage _ tag]} { - set livecheck.type "googlecode" - } elseif {[regexp {^http://www.gnu.org/software/([^/]+)} $homepage _ tag]} { - set livecheck.type "gnu" - } - } - } - if {${livecheck.type} in [split $available_types "|"]} { - # Load the defaults from _resources/port1.0/livecheck/${livecheck.type}.tcl. - set defaults_file "$types_dir/${livecheck.type}.tcl" - ui_debug "Loading the defaults from '$defaults_file'" - if {[catch {source $defaults_file} result]} { - ui_debug "$::errorInfo: result" - return -code 1 "The defaults could not be loaded from '$defaults_file'." - } - } - - if {$async && ${livecheck.type} ni {regex regexm md5}} { - return 0 - } - - if {![info exists tempfilename]} { - set tempfd [file tempfile tempfilename mports.livecheck] - if {$async} { - close $tempfd - unset tempfd - # Async fetch code appends .TMP for in-progress files. No real - # need to rename it here when it's done. - file rename $tempfilename ${tempfilename}.TMP - } - } - - # de-escape livecheck.url - set livecheck.url_str [join ${livecheck.url}] - - switch ${livecheck.type} { - "regex" - - "regexm" { - # single and multiline regex - if {!$async_done} { - ui_debug "Fetching ${livecheck.url_str}" - ui_debug "Using CURL options ${curl_options}" - } - if {$async} { - set async_job [curlwrap_async fetch_file {} $curl_options {} \ - [list ${livecheck.url_str}] $tempfilename] - append tempfilename .TMP - return 0 - } - set updated -1 - if {!$async_done && [catch {curl fetch {*}$curl_options ${livecheck.url_str} $tempfilename} error]} { - ui_error "cannot check if $subport was updated ($error)" - } else { - # let's extract the version from the file. - set foundmatch 0 - set the_re [join ${livecheck.regex}] - ui_debug "The regex is \"$the_re\"" - if {![info exists tempfd]} { - set tempfd [open $tempfilename r] - } - chan configure $tempfd -profile replace - if {${livecheck.type} eq "regexm"} { - set data [read $tempfd] - if {[regexp -nocase $the_re $data matched updated_version]} { - set foundmatch 1 - ui_debug "The regex matched \"$matched\", extracted \"$updated_version\"" - if {$updated_version ne ${livecheck.version}} { - if {[vercmp $updated_version ${livecheck.version}] > 0} { - set updated 1 - } else { - ui_error "livecheck failed for ${subport}: extracted version '$updated_version' is older than livecheck.version '${livecheck.version}'" - } - } else { - set updated 0 - } - } - } else { - set updated_version 0 - while {[gets $tempfd line] >= 0} { - set lastoff 0 - while {$lastoff >= 0 && [regexp -nocase -start $lastoff -indices $the_re $line offsets]} { - regexp -nocase -start $lastoff $the_re $line matched upver - set foundmatch 1 - if {$updated_version == 0 || [vercmp $upver $updated_version] > 0} { - set updated_version $upver - } - ui_debug "The regex matched \"$matched\", extracted \"$upver\"" - set lastoff [lindex $offsets end] - } - } - if {$foundmatch == 1} { - if {$updated_version ne ${livecheck.version}} { - if {[vercmp $updated_version ${livecheck.version}] > 0} { - set updated 1 - } else { - ui_error "livecheck failed for ${subport}: extracted version '$updated_version' is older than livecheck.version '${livecheck.version}'" - } - } else { - set updated 0 - } - } - } - if {!$foundmatch} { - ui_error "cannot check if $subport was updated (regex didn't match)" - } - } - } - "md5" { - if {!$async_done} { - ui_debug "Fetching ${livecheck.url_str}" - } - if {$async} { - set async_job [curlwrap_async fetch_file {} $curl_options {} \ - [list ${livecheck.url_str}] $tempfilename] - append tempfilename .TMP - return 0 - } - if {!$async_done && [catch {curl fetch {*}$curl_options ${livecheck.url_str} $tempfilename} error]} { - ui_error "cannot check if $subport was updated ($error)" - set updated -1 - } else { - # let's compute the md5 sum. - set dist_md5 [md5 file $tempfilename] - if {$dist_md5 ne ${livecheck.md5}} { - ui_debug "md5sum for ${livecheck.url_str}: $dist_md5" - set updated 1 - } - } - } - "moddate" { - set port_moddate [file mtime ${portpath}/Portfile] - ui_debug "Portfile modification date is [clock format $port_moddate]" - if {[catch {set updated [curl isnewer ${livecheck.url_str} $port_moddate]} error]} { - ui_error "cannot check if $subport was updated ($error)" - set updated -1 - } else { - if {!$updated} { - ui_debug "${livecheck.url_str} is older than Portfile" - } - } - } - "git" { - if {${livecheck.branch} eq {}} { - set livecheck.branch "HEAD" - } - ui_debug "Getting latest commit from ${livecheck.url_str} ${livecheck.branch}" - if {[catch {exec ${git.cmd} ls-remote ${livecheck.url_str} ${livecheck.branch}} result]} { - ui_error "cannot check if $subport was updated ($result)" - set updated -1 - } else { - set updated_version [lindex [split $result] 0] - if {$updated_version ne ${livecheck.version}} { - set updated 1 - } - } - } - "none" { - ui_debug "livecheck.type is none" - } - default { - ui_error "unknown livecheck.type ${livecheck.type}" - } - } - - if {[info exists tempfd]} { - close $tempfd - } - file delete $tempfilename - unset tempfilename - - if {${livecheck.type} ne "none"} { - if {$updated > 0} { - ui_msg "$subport seems to have been updated (port version: ${livecheck.version}, new version: $updated_version)" - } elseif {$updated == 0} { - ui_info "$subport seems to be up to date" } } } - -proc portlivecheck::livecheck_main {args} { - _livecheck_main no -} diff --git a/src/port1.0/portlivecheck_run.tcl b/src/port1.0/portlivecheck_run.tcl new file mode 100644 index 000000000..e05ebe06b --- /dev/null +++ b/src/port1.0/portlivecheck_run.tcl @@ -0,0 +1,321 @@ +# -*- 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 +# +# Copyright (c) 2007-2011, 2014, 2016 The MacPorts Project +# Copyright (c) 2005-2007 Paul Guyot , +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The MacPorts Project nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package provide portlivecheck_run 1.0 + +namespace eval portlivecheck { + +proc _livecheck_main {{async no}} { + global livecheck.url livecheck.type livecheck.md5 livecheck.regex \ + livecheck.branch livecheck.name livecheck.distname livecheck.version \ + livecheck.ignore_sslcert livecheck.compression livecheck.curloptions \ + livecheck.user_agent git.cmd homepage portpath \ + master_sites name subport + + if {${livecheck.type} eq "none"} { + if {!$async} { + ui_debug "livecheck.type is none" + } + return 0 + } + + variable async_job + variable tempfilename + + if {[info exists async_job]} { + if {$async} { + # Async fetch already started + return 0 + } + # Fetch was started asynchronously, wait for job to finish + # Loop with a reasonable timeout so we don't wait too long + # to handle events like signals. + while {![curlwrap_async_is_complete $async_job 500]} {} + lassign [curlwrap_async_result $async_job] status result + unset async_job + if {$status != 0} { + ui_error "cannot check if $subport was updated ($result)" + file delete $tempfilename + unset tempfilename + return 0 + } + set async_done 1 + } else { + set async_done 0 + } + + set updated 0 + set updated_version "unknown" + set has_master_sites [info exists master_sites] + set has_homepage [info exists homepage] + if {!$has_homepage} { + set livecheck.url {} + } + + if {!$async} { + ui_debug "Port (livecheck) version is ${livecheck.version}" + } + + set curl_options ${livecheck.curloptions} + if {[tbool livecheck.ignore_sslcert]} { + lappend curl_options "--ignore-ssl-cert" + } + if {[tbool livecheck.compression]} { + lappend curl_options "--enable-compression" + } + if {${livecheck.user_agent} ne ""} { + lappend curl_options "--user-agent" + lappend curl_options "${livecheck.user_agent}" + } + + # Check _resources/port1.0/livecheck for available types. + set types_dir [getdefaultportresourcepath "port1.0/livecheck"] + if {[catch {set available_types [glob -directory $types_dir -tails -types f *.tcl]} result]} { + return -code 1 "No available types were found. Check '$types_dir'." + } + + # Convert available_types from a list of files (e.g., { freecode.tcl + # gnu.tcl ... }) into a string in the format "type|type|..." (e.g., + # "freecode|gnu|..."). + set available_types [regsub -all {\.tcl} [join $available_types |] {}] + + if {${livecheck.type} eq "default"} { + # Determine the default type from the mirror. + if {$has_master_sites} { + foreach {master_site} ${master_sites} { + if {[regexp "^($available_types)(?::(\[^:\]+))?" ${master_site} _ site subdir]} { + set subdirs [split $subdir /] + if {[llength $subdirs] > 1} { + if {[lindex $subdirs 0] eq "project"} { + set subdir [lindex $subdirs 1] + } else { + set subdir "" + } + } + if {${subdir} ne "" && ${livecheck.name} eq "default"} { + set livecheck.name ${subdir} + } + set livecheck.type ${site} + + break + } + } + } + # If the default type cannot be determined from the mirror, use the + # fallback. + if {${livecheck.type} eq "default"} { + set livecheck.type "fallback" + } + if {$has_homepage} { + if {[regexp {^http://code.google.com/p/([^/]+)} $homepage _ tag]} { + set livecheck.type "googlecode" + } elseif {[regexp {^http://www.gnu.org/software/([^/]+)} $homepage _ tag]} { + set livecheck.type "gnu" + } + } + } + if {${livecheck.type} in [split $available_types "|"]} { + # Load the defaults from _resources/port1.0/livecheck/${livecheck.type}.tcl. + set defaults_file "$types_dir/${livecheck.type}.tcl" + ui_debug "Loading the defaults from '$defaults_file'" + if {[catch {source $defaults_file} result]} { + ui_debug "$::errorInfo: result" + return -code 1 "The defaults could not be loaded from '$defaults_file'." + } + } + + if {$async && ${livecheck.type} ni {regex regexm md5}} { + return 0 + } + + if {![info exists tempfilename]} { + set tempfd [file tempfile tempfilename mports.livecheck] + if {$async} { + close $tempfd + unset tempfd + # Async fetch code appends .TMP for in-progress files. No real + # need to rename it here when it's done. + file rename $tempfilename ${tempfilename}.TMP + } + } + + # de-escape livecheck.url + set livecheck.url_str [join ${livecheck.url}] + + switch ${livecheck.type} { + "regex" - + "regexm" { + # single and multiline regex + if {!$async_done} { + ui_debug "Fetching ${livecheck.url_str}" + ui_debug "Using CURL options ${curl_options}" + } + if {$async} { + set async_job [curlwrap_async fetch_file {} $curl_options {} \ + [list ${livecheck.url_str}] $tempfilename] + append tempfilename .TMP + return 0 + } + set updated -1 + if {!$async_done && [catch {curl fetch {*}$curl_options ${livecheck.url_str} $tempfilename} error]} { + ui_error "cannot check if $subport was updated ($error)" + } else { + # let's extract the version from the file. + set foundmatch 0 + set the_re [join ${livecheck.regex}] + ui_debug "The regex is \"$the_re\"" + if {![info exists tempfd]} { + set tempfd [open $tempfilename r] + } + chan configure $tempfd -profile replace + if {${livecheck.type} eq "regexm"} { + set data [read $tempfd] + if {[regexp -nocase $the_re $data matched updated_version]} { + set foundmatch 1 + ui_debug "The regex matched \"$matched\", extracted \"$updated_version\"" + if {$updated_version ne ${livecheck.version}} { + if {[vercmp $updated_version ${livecheck.version}] > 0} { + set updated 1 + } else { + ui_error "livecheck failed for ${subport}: extracted version '$updated_version' is older than livecheck.version '${livecheck.version}'" + } + } else { + set updated 0 + } + } + } else { + set updated_version 0 + while {[gets $tempfd line] >= 0} { + set lastoff 0 + while {$lastoff >= 0 && [regexp -nocase -start $lastoff -indices $the_re $line offsets]} { + regexp -nocase -start $lastoff $the_re $line matched upver + set foundmatch 1 + if {$updated_version == 0 || [vercmp $upver $updated_version] > 0} { + set updated_version $upver + } + ui_debug "The regex matched \"$matched\", extracted \"$upver\"" + set lastoff [lindex $offsets end] + } + } + if {$foundmatch == 1} { + if {$updated_version ne ${livecheck.version}} { + if {[vercmp $updated_version ${livecheck.version}] > 0} { + set updated 1 + } else { + ui_error "livecheck failed for ${subport}: extracted version '$updated_version' is older than livecheck.version '${livecheck.version}'" + } + } else { + set updated 0 + } + } + } + if {!$foundmatch} { + ui_error "cannot check if $subport was updated (regex didn't match)" + } + } + } + "md5" { + if {!$async_done} { + ui_debug "Fetching ${livecheck.url_str}" + } + if {$async} { + set async_job [curlwrap_async fetch_file {} $curl_options {} \ + [list ${livecheck.url_str}] $tempfilename] + append tempfilename .TMP + return 0 + } + if {!$async_done && [catch {curl fetch {*}$curl_options ${livecheck.url_str} $tempfilename} error]} { + ui_error "cannot check if $subport was updated ($error)" + set updated -1 + } else { + # let's compute the md5 sum. + set dist_md5 [md5 file $tempfilename] + if {$dist_md5 ne ${livecheck.md5}} { + ui_debug "md5sum for ${livecheck.url_str}: $dist_md5" + set updated 1 + } + } + } + "moddate" { + set port_moddate [file mtime ${portpath}/Portfile] + ui_debug "Portfile modification date is [clock format $port_moddate]" + if {[catch {set updated [curl isnewer ${livecheck.url_str} $port_moddate]} error]} { + ui_error "cannot check if $subport was updated ($error)" + set updated -1 + } else { + if {!$updated} { + ui_debug "${livecheck.url_str} is older than Portfile" + } + } + } + "git" { + if {${livecheck.branch} eq {}} { + set livecheck.branch "HEAD" + } + ui_debug "Getting latest commit from ${livecheck.url_str} ${livecheck.branch}" + if {[catch {exec ${git.cmd} ls-remote ${livecheck.url_str} ${livecheck.branch}} result]} { + ui_error "cannot check if $subport was updated ($result)" + set updated -1 + } else { + set updated_version [lindex [split $result] 0] + if {$updated_version ne ${livecheck.version}} { + set updated 1 + } + } + } + "none" { + ui_debug "livecheck.type is none" + } + default { + ui_error "unknown livecheck.type ${livecheck.type}" + } + } + + if {[info exists tempfd]} { + close $tempfd + } + file delete $tempfilename + unset tempfilename + + if {${livecheck.type} ne "none"} { + if {$updated > 0} { + ui_msg "$subport seems to have been updated (port version: ${livecheck.version}, new version: $updated_version)" + } elseif {$updated == 0} { + ui_info "$subport seems to be up to date" + } + } +} + +proc livecheck_main {args} { + _livecheck_main no +} + +} diff --git a/src/port1.0/portload.tcl b/src/port1.0/portload.tcl index c3bb348b2..af65cdfd8 100644 --- a/src/port1.0/portload.tcl +++ b/src/port1.0/portload.tcl @@ -1,92 +1,14 @@ -# et:ts=4 -# portload.tcl -# -# Copyright (c) 2007, 2009, 2011 The MacPorts Project -# Copyright (c) 2007 James D. Berry -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of The MacPorts Project nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# +# -*- 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 + +# License: see portload_run.tcl package provide portload 1.0 -package require portutil 1.0 set org.macports.load [target_new org.macports.load portload::load_main] target_runtype ${org.macports.load} always target_state ${org.macports.load} no target_provides ${org.macports.load} load target_requires ${org.macports.load} main - -namespace eval portload { -} +target_runpkg ${org.macports.load} portload_run options load.asroot - -set_ui_prefix - -proc portload::load_main {args} { - global UI_PREFIX prefix subport sudo_user \ - portstartupitem::load_only portstartupitem::autostart_only \ - portutil::autoconf::launchctl_path - - portstartupitem::foreach_startupitem { - if {(![info exists load_only] || $si_name in ${load_only}) - && (![info exists autostart_only] || !$autostart_only || $si_autostart)} { - if {$si_install} { - set path /Library/${si_location}/${si_plist} - } else { - set path ${prefix}/etc/${si_location}/${si_plist} - } - if {$launchctl_path eq ""} { - return -code error [format [msgcat::mc "launchctl command was not found by configure"]] - } elseif {![file exists $path]} { - return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path] - } else { - set skip 0 - if {$si_location eq "LaunchDaemons"} { - if {[getuid] == 0} { - set uid 0 - } else { - ui_warn [format [msgcat::mc "Skipping load of startupitem '%s' for %s, root privileges required"] $si_name $subport] - set skip 1 - } - } elseif {[getuid] == 0} { - if {[info exists sudo_user]} { - set uid [name_to_uid $sudo_user] - } else { - ui_warn [format [msgcat::mc "Skipping load of per-user startupitem '%s' for %s (running as root)"] $si_name $subport] - set skip 1 - } - } else { - set uid [getuid] - } - if {!$skip} { - ui_notice "$UI_PREFIX [format [msgcat::mc "Loading startupitem '%s' for %s"] $si_name $subport]" - exec_as_uid $uid {system "$launchctl_path load -w $path"} - } - } - } - } -} diff --git a/src/port1.0/portload_run.tcl b/src/port1.0/portload_run.tcl new file mode 100644 index 000000000..c06a4bee7 --- /dev/null +++ b/src/port1.0/portload_run.tcl @@ -0,0 +1,80 @@ +# -*- 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 +# +# Copyright (c) 2007, 2009, 2011 The MacPorts Project +# Copyright (c) 2007 James D. Berry +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The MacPorts Project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +package provide portload_run 1.0 + +namespace eval portload { + +proc load_main {args} { + global UI_PREFIX prefix subport sudo_user \ + portstartupitem::load_only portstartupitem::autostart_only \ + portutil::autoconf::launchctl_path + + portstartupitem::foreach_startupitem { + if {(![info exists load_only] || $si_name in ${load_only}) + && (![info exists autostart_only] || !$autostart_only || $si_autostart)} { + if {$si_install} { + set path /Library/${si_location}/${si_plist} + } else { + set path ${prefix}/etc/${si_location}/${si_plist} + } + if {$launchctl_path eq ""} { + return -code error [format [msgcat::mc "launchctl command was not found by configure"]] + } elseif {![file exists $path]} { + return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path] + } else { + set skip 0 + if {$si_location eq "LaunchDaemons"} { + if {[getuid] == 0} { + set uid 0 + } else { + ui_warn [format [msgcat::mc "Skipping load of startupitem '%s' for %s, root privileges required"] $si_name $subport] + set skip 1 + } + } elseif {[getuid] == 0} { + if {[info exists sudo_user]} { + set uid [name_to_uid $sudo_user] + } else { + ui_warn [format [msgcat::mc "Skipping load of per-user startupitem '%s' for %s (running as root)"] $si_name $subport] + set skip 1 + } + } else { + set uid [getuid] + } + if {!$skip} { + ui_notice "$UI_PREFIX [format [msgcat::mc "Loading startupitem '%s' for %s"] $si_name $subport]" + exec_as_uid $uid {system "$launchctl_path load -w $path"} + } + } + } + } +} + +} diff --git a/src/port1.0/portmirror.tcl b/src/port1.0/portmirror.tcl index 862a7de02..230e3ff3a 100644 --- a/src/port1.0/portmirror.tcl +++ b/src/port1.0/portmirror.tcl @@ -1,111 +1,12 @@ -# et:ts=4 -# portmirror.tcl -# -# Copyright (c) 2007-2011 The MacPorts Project -# Copyright (c) 2006 Paul Guyot , -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of The MacPorts Project nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# +# -*- 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 + +# License: see portmirror_run.tcl package provide portmirror 1.0 -package require portutil 1.0 -package require Pextlib 1.0 -package require portfetch 1.0 -package require portchecksum 1.0 set org.macports.mirror [target_new org.macports.mirror portmirror::mirror_main] target_runtype ${org.macports.mirror} always target_state ${org.macports.mirror} no target_provides ${org.macports.mirror} mirror target_requires ${org.macports.mirror} main - -namespace eval portmirror { -} - -# Mirror is a target that fetches & checksums files and delete them -# if the checksum isn't correct. -# It also records the path in a database. - -# Utility function to delete fetched files. -proc portmirror::fetch_deletefiles {args} { - global distpath portfetch::fetch_urls - foreach {url_var distfile} $fetch_urls { - if {[file isfile $distpath/$distfile]} { - file delete ${distpath}/${distfile} - } - } -} - -# Utility function to add files to a list of fetched files. -proc portmirror::fetch_addfilestomap {filemapname} { - global distpath $filemapname portfetch::fetch_urls - foreach {url_var distfile} $fetch_urls { - if {[file isfile $distpath/$distfile]} { - filemap set $filemapname $distpath/$distfile 1 - } - } -} - -proc portmirror::mirror_main {args} { - global fetch.type mirror_filemap portdbpath subport license - - if {[lsearch -exact -nocase $license "nomirror"] >= 0} { - ui_info "Not mirroring $subport due to license NoMirror" - return - } - - set mirror_filemap_path [file join $portdbpath distfiles_mirror.db] - exec_with_available_privileges { - filemap open mirror_filemap $mirror_filemap_path - } - - # Check the distfiles if it's a regular fetch phase. - if {${fetch.type} eq "standard"} { - # fetch the files. - portfetch::fetch_init $args - portfetch::fetch_start $args - portfetch::fetch_main $args - - # checksum the files. - if {[catch {portchecksum::checksum_main $args} result]} { - # delete the files. - fetch_deletefiles $args - exec_with_available_privileges { - filemap close mirror_filemap - } - error "portmirror: checksum failed: $result" - } - - # add the list of files. - fetch_addfilestomap mirror_filemap - } - - # close the filemap. - exec_with_available_privileges { - filemap close mirror_filemap - } -} +target_runpkg ${org.macports.mirror} portmirror_run diff --git a/src/port1.0/portmirror_run.tcl b/src/port1.0/portmirror_run.tcl new file mode 100644 index 000000000..2c16435a5 --- /dev/null +++ b/src/port1.0/portmirror_run.tcl @@ -0,0 +1,102 @@ +# -*- 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 +# +# Copyright (c) 2007-2011 The MacPorts Project +# Copyright (c) 2006 Paul Guyot , +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The MacPorts Project nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package provide portmirror_run 1.0 +package require portfetch_run 1.0 +package require portchecksum_run 1.0 + +# Mirror is a target that fetches & checksums files and delete them +# if the checksum isn't correct. +# It also records the path in a database. + +namespace eval portmirror { + +# Utility function to delete fetched files. +proc fetch_deletefiles {args} { + global distpath portfetch::fetch_urls + foreach {url_var distfile} $fetch_urls { + if {[file isfile $distpath/$distfile]} { + file delete ${distpath}/${distfile} + } + } +} + +# Utility function to add files to a list of fetched files. +proc fetch_addfilestomap {filemapname} { + global distpath $filemapname portfetch::fetch_urls + foreach {url_var distfile} $fetch_urls { + if {[file isfile $distpath/$distfile]} { + filemap set $filemapname $distpath/$distfile 1 + } + } +} + +proc mirror_main {args} { + global fetch.type mirror_filemap portdbpath subport license + + if {[lsearch -exact -nocase $license "nomirror"] >= 0} { + ui_info "Not mirroring $subport due to license NoMirror" + return + } + + set mirror_filemap_path [file join $portdbpath distfiles_mirror.db] + exec_with_available_privileges { + filemap open mirror_filemap $mirror_filemap_path + } + + # Check the distfiles if it's a regular fetch phase. + if {${fetch.type} eq "standard"} { + # fetch the files. + portfetch::fetch_init $args + portfetch::fetch_start $args + portfetch::fetch_main $args + + # checksum the files. + if {[catch {portchecksum::checksum_main $args} result]} { + # delete the files. + fetch_deletefiles $args + exec_with_available_privileges { + filemap close mirror_filemap + } + error "portmirror: checksum failed: $result" + } + + # add the list of files. + fetch_addfilestomap mirror_filemap + } + + # close the filemap. + exec_with_available_privileges { + filemap close mirror_filemap + } +} + +} diff --git a/src/port1.0/portpatch.tcl b/src/port1.0/portpatch.tcl index 966f58d14..3e01aa1e0 100644 --- a/src/port1.0/portpatch.tcl +++ b/src/port1.0/portpatch.tcl @@ -1,47 +1,17 @@ -# et:ts=4 -# portpatch.tcl -# -# Copyright (c) 2004, 2006-2007, 2009-2011 The MacPorts Project -# Copyright (c) 2002 - 2003 Apple Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of Apple Inc. nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# +# -*- 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 + +# License: see portpatch_run.tcl package provide portpatch 1.0 -package require portutil 1.0 set org.macports.patch [target_new org.macports.patch portpatch::patch_main] target_provides ${org.macports.patch} patch target_requires ${org.macports.patch} main fetch checksum extract +target_runpkg ${org.macports.patch} portpatch_run namespace eval portpatch { } -set_ui_prefix - # Add command patch commands patch @@ -66,49 +36,3 @@ proc portpatch::build_getpatchtype {args} { } } } - -proc portpatch::patch_main {args} { - global UI_PREFIX - - # First make sure that patchfiles exists and isn't stubbed out. - if {![exists patchfiles] || [option patchfiles] eq ""} { - return 0 - } - - ui_notice "$UI_PREFIX [format [msgcat::mc "Applying patches to %s"] [option subport]]" - - foreach patch [option patchfiles] { - set patch_file [getdistname $patch] - if {[file exists [option filespath]/$patch_file]} { - lappend patchlist [option filespath]/$patch_file - } elseif {[file exists [option distpath]/$patch_file]} { - lappend patchlist [option distpath]/$patch_file - } else { - return -code error [format [msgcat::mc "Patch file %s is missing"] $patch] - } - } - if {![info exists patchlist]} { - return -code error [msgcat::mc "Patch files missing"] - } - - set gzcat "[findBinary gzip $::portutil::autoconf::gzip_path] -dc" - set bzcat "[findBinary bzip2 $::portutil::autoconf::bzip2_path] -dc" - catch {set xzcat "[findBinary xz $::portutil::autoconf::xz_path] -dc"} - - foreach patch $patchlist { - ui_info "$UI_PREFIX [format [msgcat::mc "Applying %s"] [file tail $patch]]" - switch -- [file extension $patch] { - .Z - - .gz {command_exec patch "$gzcat \"$patch\" | (" ")"} - .bz2 {command_exec patch "$bzcat \"$patch\" | (" ")"} - .xz { - if {[info exists xzcat]} { - command_exec patch "$xzcat \"$patch\" | (" ")" - } else { - return -code error [msgcat::mc "xz binary not found; port needs to add 'depends_patch bin:xz:xz'"] - }} - default {command_exec patch "" "< '$patch'"} - } - } - return 0 -} diff --git a/src/port1.0/portpatch_run.tcl b/src/port1.0/portpatch_run.tcl new file mode 100644 index 000000000..0a1defcab --- /dev/null +++ b/src/port1.0/portpatch_run.tcl @@ -0,0 +1,81 @@ +# -*- 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 +# +# Copyright (c) 2004, 2006-2007, 2009-2011 The MacPorts Project +# Copyright (c) 2002 - 2003 Apple Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Apple Inc. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +package provide portpatch_run 1.0 + +namespace eval portpatch { + +proc patch_main {args} { + global UI_PREFIX + + # First make sure that patchfiles exists and isn't stubbed out. + if {![exists patchfiles] || [option patchfiles] eq ""} { + return 0 + } + + ui_notice "$UI_PREFIX [format [msgcat::mc "Applying patches to %s"] [option subport]]" + + foreach patch [option patchfiles] { + set patch_file [getdistname $patch] + if {[file exists [option filespath]/$patch_file]} { + lappend patchlist [option filespath]/$patch_file + } elseif {[file exists [option distpath]/$patch_file]} { + lappend patchlist [option distpath]/$patch_file + } else { + return -code error [format [msgcat::mc "Patch file %s is missing"] $patch] + } + } + if {![info exists patchlist]} { + return -code error [msgcat::mc "Patch files missing"] + } + + set gzcat "[findBinary gzip $::portutil::autoconf::gzip_path] -dc" + set bzcat "[findBinary bzip2 $::portutil::autoconf::bzip2_path] -dc" + catch {set xzcat "[findBinary xz $::portutil::autoconf::xz_path] -dc"} + + foreach patch $patchlist { + ui_info "$UI_PREFIX [format [msgcat::mc "Applying %s"] [file tail $patch]]" + switch -- [file extension $patch] { + .Z - + .gz {command_exec patch "$gzcat \"$patch\" | (" ")"} + .bz2 {command_exec patch "$bzcat \"$patch\" | (" ")"} + .xz { + if {[info exists xzcat]} { + command_exec patch "$xzcat \"$patch\" | (" ")" + } else { + return -code error [msgcat::mc "xz binary not found; port needs to add 'depends_patch bin:xz:xz'"] + }} + default {command_exec patch "" "< '$patch'"} + } + } + return 0 +} + +} diff --git a/src/port1.0/portprogress.tcl b/src/port1.0/portprogress.tcl index f0165e73b..85dfba728 100644 --- a/src/port1.0/portprogress.tcl +++ b/src/port1.0/portprogress.tcl @@ -28,8 +28,6 @@ # POSSIBILITY OF SUCH DAMAGE. package provide portprogress 1.0 -package require portutil 1.0 -package require Pextlib 1.0 namespace eval portprogress { # The time in milliseconds to wait before we switch our progress bar from diff --git a/src/port1.0/portreload.tcl b/src/port1.0/portreload.tcl index f4fe47614..d4062b8f1 100644 --- a/src/port1.0/portreload.tcl +++ b/src/port1.0/portreload.tcl @@ -1,97 +1,14 @@ # -*- 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 -# -# Copyright (c) 2007-2014 The MacPorts Project -# Copyright (c) 2007 James D. Berry -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of The MacPorts Project nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# + +# License: see portreload_run.tcl package provide portreload 1.0 -package require portutil 1.0 set org.macports.reload [target_new org.macports.reload portreload::reload_main] target_runtype ${org.macports.reload} always target_state ${org.macports.reload} no target_provides ${org.macports.reload} reload target_requires ${org.macports.reload} main - -namespace eval portreload { -} +target_runpkg ${org.macports.reload} portreload_run options reload.asroot - -set_ui_prefix - -proc portreload::reload_main {args} { - global UI_PREFIX prefix subport sudo_user \ - portutil::autoconf::launchctl_path - - portstartupitem::foreach_startupitem { - if {$si_install} { - set path /Library/${si_location}/${si_plist} - } else { - set path ${prefix}/etc/${si_location}/${si_plist} - } - if {$launchctl_path eq ""} { - return -code error [format [msgcat::mc "launchctl command was not found by configure"]] - } elseif {![file exists $path]} { - return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path] - } else { - set skip 0 - if {$si_location eq "LaunchDaemons"} { - if {[getuid] == 0} { - set uid 0 - } else { - ui_warn [format [msgcat::mc "Skipping reload of startupitem '%s' for %s, root privileges required"] $si_name $subport] - set skip 1 - } - } elseif {[getuid] == 0} { - if {[info exists sudo_user]} { - set uid [name_to_uid $sudo_user] - } else { - ui_warn [format [msgcat::mc "Skipping reload of per-user startupitem '%s' for %s (running as root)"] $si_name $subport] - set skip 1 - } - } else { - set uid [getuid] - } - if {!$skip} { - ui_notice "$UI_PREFIX [format [msgcat::mc "Reloading startupitem '%s' for %s"] $si_name $subport]" - # Basically run port unload; port load. - exec_as_uid $uid { - system "$launchctl_path unload -w $path" - # Let's wait a second. #36054 suggests some ports have problems - # when they are re-started too quickly, and I hope the second - # doesn't hurt too much. - after 1000 - system "$launchctl_path load -w $path" - } - } - } - } - - return -} diff --git a/src/port1.0/portreload_run.tcl b/src/port1.0/portreload_run.tcl new file mode 100644 index 000000000..6f26d469c --- /dev/null +++ b/src/port1.0/portreload_run.tcl @@ -0,0 +1,86 @@ +# -*- 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 +# +# Copyright (c) 2007-2014 The MacPorts Project +# Copyright (c) 2007 James D. Berry +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The MacPorts Project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +package provide portreload_run 1.0 + +namespace eval portreload { + +proc reload_main {args} { + global UI_PREFIX prefix subport sudo_user \ + portutil::autoconf::launchctl_path + + portstartupitem::foreach_startupitem { + if {$si_install} { + set path /Library/${si_location}/${si_plist} + } else { + set path ${prefix}/etc/${si_location}/${si_plist} + } + if {$launchctl_path eq ""} { + return -code error [format [msgcat::mc "launchctl command was not found by configure"]] + } elseif {![file exists $path]} { + return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path] + } else { + set skip 0 + if {$si_location eq "LaunchDaemons"} { + if {[getuid] == 0} { + set uid 0 + } else { + ui_warn [format [msgcat::mc "Skipping reload of startupitem '%s' for %s, root privileges required"] $si_name $subport] + set skip 1 + } + } elseif {[getuid] == 0} { + if {[info exists sudo_user]} { + set uid [name_to_uid $sudo_user] + } else { + ui_warn [format [msgcat::mc "Skipping reload of per-user startupitem '%s' for %s (running as root)"] $si_name $subport] + set skip 1 + } + } else { + set uid [getuid] + } + if {!$skip} { + ui_notice "$UI_PREFIX [format [msgcat::mc "Reloading startupitem '%s' for %s"] $si_name $subport]" + # Basically run port unload; port load. + exec_as_uid $uid { + system "$launchctl_path unload -w $path" + # Let's wait a second. #36054 suggests some ports have problems + # when they are re-started too quickly, and I hope the second + # doesn't hurt too much. + after 1000 + system "$launchctl_path load -w $path" + } + } + } + } + + return +} + +} diff --git a/src/port1.0/portstartupitem.tcl b/src/port1.0/portstartupitem.tcl index b2d6893c4..2f72358ee 100644 --- a/src/port1.0/portstartupitem.tcl +++ b/src/port1.0/portstartupitem.tcl @@ -1,39 +1,12 @@ -# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4 -# portstartupitem.tcl -# -# Copyright (c) 2004-2014, 2016-2018, 2020 The MacPorts Project -# Copyright (c) 2006-2007 James D. Berry -# Copyright (c) 2004,2005 Markus W. Weissman -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of The MacPorts Project nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# +# -*- 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 +# License: see portstartupitem_run.tcl + +package provide portstartupitem 1.0 + +namespace eval portstartupitem { +} -# # Newly added keys: # # startupitem.executable the command to start the executable @@ -81,12 +54,6 @@ # Enable additional debug logging # - for launchd, sets the Debug key to true -package provide portstartupitem 1.0 -package require portutil 1.0 - -namespace eval portstartupitem { -} - options startupitems startupitem.autostart startupitem.debug \ startupitem.create startupitem.custom_file \ startupitem.executable startupitem.group \ @@ -123,8 +90,6 @@ default startupitem.user {} default startupitem.daemondo.verbosity 1 -set_ui_prefix - # Calculate a default value for startupitem.type proc portstartupitem::get_startupitem_type {} { global system_options os.platform startupitem.create @@ -268,302 +233,3 @@ proc portstartupitem::add_notes {} { # Register the above procedure as a callback after Portfile evaluation port::register_callback portstartupitem::add_notes - -# Helper: link given .plist into the correct location -proc portstartupitem::install_darwin_launchd {srcpath dstdir install} { - global destroot prefix - if {[getuid] == 0 && $install} { - file mkdir ${destroot}/Library/${dstdir} - ln -sf $srcpath ${destroot}/Library/${dstdir} - } else { - ln -sf $srcpath ${destroot}${prefix}/etc/${dstdir} - } -} - -proc portstartupitem::startupitem_create_darwin_launchd {attrs} { - global UI_PREFIX prefix destroot destroot.keepdirs subport macosx_deployment_target - - set uniquename [dict get $attrs uniquename] - set plistname [dict get $attrs plist] - set daemondest [dict get $attrs location] - set itemdir ${prefix}/etc/${daemondest}/${uniquename} - - file mkdir ${destroot}${itemdir} - if {[getuid] == 0} { - file attributes ${destroot}${itemdir} -owner root -group wheel - } - - if {[dict get $attrs custom_file] ne ""} { - # The port is supplying its own plist - file copy [dict get $attrs custom_file] ${destroot}${itemdir}/${plistname} - install_darwin_launchd ${itemdir}/${plistname} $daemondest [dict get $attrs install] - return - } - - set scriptdir ${prefix}/etc/startup - - set itemname [dict get $attrs name] - set username [dict get $attrs user] - set groupname [dict get $attrs group] - set args [list \ - "${prefix}/bin/daemondo" \ - "--label=${itemname}" \ - ] - - if {[dict get $attrs executable] ne "" && - [dict get $attrs init] eq "" && - [dict get $attrs start] eq "" && - [dict get $attrs stop] eq "" && - [dict get $attrs restart] eq ""} { - - # An executable is specified, and there is no init, start, stop, or restart - # code; so we don't need a wrapper script - set args [concat $args "--start-cmd" [dict get $attrs executable] ";"] - - } else { - - # No executable was specified, or there was an init, start, stop, or restart - # option, so we do need a wrapper script - - set wrappername ${itemname}.wrapper - set wrapper "${itemdir}/${wrappername}" - - if {[dict get $attrs start] eq ""} { - dict set attrs start [list "sh ${scriptdir}/${subport}.sh start"] - } - if {[dict get $attrs stop] eq ""} { - dict set attrs stop [list "sh ${scriptdir}/${subport}.sh stop"] - } - if {[dict get $attrs restart] eq ""} { - dict set attrs restart [list Stop Start] - } - - lappend args \ - "--start-cmd" ${wrapper} start ";" \ - "--stop-cmd" ${wrapper} stop ";" \ - "--restart-cmd" ${wrapper} restart ";" - - # Create the wrapper script - set item [open "${destroot}${wrapper}" w 0755] - if {[getuid] == 0} { - file attributes "${destroot}${wrapper}" -owner root -group wheel - } - - puts ${item} "#!/bin/sh" - puts ${item} "#" - puts ${item} "# MacPorts generated daemondo support script" - puts ${item} "#" - puts ${item} "" - - puts ${item} "#" - puts ${item} "# Init" - puts ${item} "#" - puts ${item} "prefix=$prefix" - foreach line [dict get $attrs init] { puts ${item} ${line} } - puts ${item} "" - - puts ${item} "#" - puts ${item} "# Start" - puts ${item} "#" - puts ${item} "Start()" - puts ${item} "\{" - foreach line [dict get $attrs start] { puts ${item} "\t${line}" } - puts ${item} "\}" - puts ${item} "" - - puts ${item} "#" - puts ${item} "# Stop" - puts ${item} "#" - puts ${item} "Stop()" - puts ${item} "\{" - foreach line [dict get $attrs stop] { puts ${item} "\t${line}" } - puts ${item} "\}" - puts ${item} "" - - puts ${item} "#" - puts ${item} "# Restart" - puts ${item} "#" - puts ${item} "Restart()" - puts ${item} "\{" - foreach line [dict get $attrs restart] { puts ${item} "\t${line}" } - puts ${item} "\}" - puts ${item} "" - - puts ${item} "#" - puts ${item} "# Run" - puts ${item} "#" - puts ${item} "Run()" - puts ${item} "\{" - puts ${item} "case \$1 in" - puts ${item} " start ) Start ;;" - puts ${item} " stop ) Stop ;;" - puts ${item} " restart) Restart ;;" - puts ${item} " * ) echo \"\$0: unknown argument: \$1\";;" - puts ${item} "esac" - puts ${item} "\}" - puts ${item} "" - - puts ${item} "#" - puts ${item} "# Run a phase based on the selector" - puts ${item} "#" - puts ${item} "Run \$1" - puts ${item} "" - - close ${item} - } - - if {[dict get $attrs netchange]} { - lappend args "--restart-netchange" - } - - # To log events then tell daemondo to log at verbosity=n - if {[dict get $attrs logevents]} { - lappend args "--verbosity=[option startupitem.daemondo.verbosity]" - } - - # If pidfile was specified, translate it for daemondo. - # - # There are four cases: - # (1) none - # (2) auto [pidfilename] - # (3) clean [pidfilename] - # (4) manual [pidfilename] - # - set pidfileArgCnt [llength [dict get $attrs pidfile]] - if {${pidfileArgCnt} > 0} { - if { $pidfileArgCnt == 1 } { - set pidFile "${prefix}/var/run/${itemname}.pid" - if {"${destroot}${prefix}/var/run" ni ${destroot.keepdirs}} { - lappend destroot.keepdirs "${destroot}${prefix}/var/run" - } - } else { - set pidFile [lindex [dict get $attrs pidfile] 1] - } - - if {${pidfileArgCnt} > 2} { - ui_error "$UI_PREFIX [msgcat::mc "Invalid parameter count to startupitem.pidfile: 2 expected, %d found" ${pidfileArgCnt}]" - } - - # Translate into appropriate arguments to daemondo - set pidStyle [lindex [dict get $attrs pidfile] 0] - switch -- ${pidStyle} { - none { lappend args "--pid=none" } - auto { lappend args "--pid=fileauto" "--pidfile" ${pidFile} } - clean { lappend args "--pid=fileclean" "--pidfile" ${pidFile} } - manual { lappend args "--pid=exec" "--pidfile" ${pidFile} } - default { - ui_error "$UI_PREFIX [msgcat::mc "Unknown pidfile style %s presented to startupitem.pidfile" ${pidStyle}]" - } - } - } else { - if {[dict get $attrs executable] ne ""} { - lappend args "--pid=exec" - } else { - lappend args "--pid=none" - } - } - - # Create the plist file - set plist [open "${destroot}${itemdir}/${plistname}" w 0644] - - puts ${plist} "" - puts ${plist} "" - puts ${plist} "" - puts ${plist} "" - - puts ${plist} "Label${uniquename}" - - puts ${plist} "ProgramArguments" - puts ${plist} "" - foreach arg ${args} { puts ${plist} "\t${arg}" } - puts ${plist} "" - - puts ${plist} "Disabled" - puts ${plist} "KeepAlive" - - if {$username ne ""} { - puts ${plist} "UserName$username" - } - - if {$groupname ne ""} { - puts ${plist} "GroupName$groupname" - } - - if {[dict get $attrs logfile] ne ""} { - puts ${plist} "StandardOutPath[dict get $attrs logfile]" - } - - if {[dict get $attrs logfile.stderr] ne ""} { - puts ${plist} "StandardErrorPath[dict get $attrs logfile.stderr]" - } - - if {[dict get $attrs debug]} { - puts ${plist} "Debug" - } - - puts ${plist} "" - puts ${plist} "" - - close ${plist} - - install_darwin_launchd ${itemdir}/${plistname} $daemondest [dict get $attrs install] -} - -proc portstartupitem::startupitem_create {} { - global UI_PREFIX - - foreach_startupitem { - if {${si_type} ne "none" && ([tbool si_create] || $si_custom_file ne "")} { - if {[tbool si_create]} { - ui_debug "Creating ${si_type} control script '$si_name'" - } else { - ui_debug "Installing ${si_type} control script '$si_name'" - } - - switch -- ${si_type} { - launchd { startupitem_create_darwin_launchd $si_dict } - default { ui_error "$UI_PREFIX [msgcat::mc "Unrecognized startupitem type %s" ${si_type}]" } - } - } - } -} - -# Check if this port's startupitems are loaded -# Returns: list of loaded startupitems -proc portstartupitem::loaded {} { - set launchctl_path ${::portutil::autoconf::launchctl_path} - if {$launchctl_path eq ""} { - # assuming not loaded if there's no launchctl - return [list] - } - set ret [list] - global os.major sudo_user - foreach_startupitem { - if {$si_type ne "launchd"} { - continue - } - if {$si_location eq "LaunchDaemons" && [getuid] == 0} { - set uid 0 - } elseif {[info exists sudo_user]} { - set uid [name_to_uid $sudo_user] - } else { - set uid [getuid] - } - if {${os.major} >= 14} { - if {$si_location eq "LaunchDaemons"} { - set domain system - } else { - set domain gui/${uid} - } - if {![catch {exec -ignorestderr $launchctl_path print ${domain}/${si_uniquename} >&/dev/null}]} { - lappend ret $si_name - } - } else { - if {![catch {exec_as_uid $uid {system "$launchctl_path list ${si_uniquename} > /dev/null"}}]} { - lappend ret $si_name - } - } - } - return $ret -} diff --git a/src/port1.0/portstartupitem_run.tcl b/src/port1.0/portstartupitem_run.tcl new file mode 100644 index 000000000..19cf02a2e --- /dev/null +++ b/src/port1.0/portstartupitem_run.tcl @@ -0,0 +1,336 @@ +# -*- 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 +# +# Copyright (c) 2004-2014, 2016-2018, 2020 The MacPorts Project +# Copyright (c) 2006-2007 James D. Berry +# Copyright (c) 2004,2005 Markus W. Weissman +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The MacPorts Project nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package provide portstartupitem_run 1.0 + +namespace eval portstartupitem { + +# Helper: link given .plist into the correct location +proc install_darwin_launchd {srcpath dstdir install} { + global destroot prefix + if {[getuid] == 0 && $install} { + file mkdir ${destroot}/Library/${dstdir} + ln -sf $srcpath ${destroot}/Library/${dstdir} + } else { + ln -sf $srcpath ${destroot}${prefix}/etc/${dstdir} + } +} + +proc startupitem_create_darwin_launchd {attrs} { + global UI_PREFIX prefix destroot destroot.keepdirs subport macosx_deployment_target + + set uniquename [dict get $attrs uniquename] + set plistname [dict get $attrs plist] + set daemondest [dict get $attrs location] + set itemdir ${prefix}/etc/${daemondest}/${uniquename} + + file mkdir ${destroot}${itemdir} + if {[getuid] == 0} { + file attributes ${destroot}${itemdir} -owner root -group wheel + } + + if {[dict get $attrs custom_file] ne ""} { + # The port is supplying its own plist + file copy [dict get $attrs custom_file] ${destroot}${itemdir}/${plistname} + install_darwin_launchd ${itemdir}/${plistname} $daemondest [dict get $attrs install] + return + } + + set scriptdir ${prefix}/etc/startup + + set itemname [dict get $attrs name] + set username [dict get $attrs user] + set groupname [dict get $attrs group] + set args [list \ + "${prefix}/bin/daemondo" \ + "--label=${itemname}" \ + ] + + if {[dict get $attrs executable] ne "" && + [dict get $attrs init] eq "" && + [dict get $attrs start] eq "" && + [dict get $attrs stop] eq "" && + [dict get $attrs restart] eq ""} { + + # An executable is specified, and there is no init, start, stop, or restart + # code; so we don't need a wrapper script + set args [concat $args "--start-cmd" [dict get $attrs executable] ";"] + + } else { + + # No executable was specified, or there was an init, start, stop, or restart + # option, so we do need a wrapper script + + set wrappername ${itemname}.wrapper + set wrapper "${itemdir}/${wrappername}" + + if {[dict get $attrs start] eq ""} { + dict set attrs start [list "sh ${scriptdir}/${subport}.sh start"] + } + if {[dict get $attrs stop] eq ""} { + dict set attrs stop [list "sh ${scriptdir}/${subport}.sh stop"] + } + if {[dict get $attrs restart] eq ""} { + dict set attrs restart [list Stop Start] + } + + lappend args \ + "--start-cmd" ${wrapper} start ";" \ + "--stop-cmd" ${wrapper} stop ";" \ + "--restart-cmd" ${wrapper} restart ";" + + # Create the wrapper script + set item [open "${destroot}${wrapper}" w 0755] + if {[getuid] == 0} { + file attributes "${destroot}${wrapper}" -owner root -group wheel + } + + puts ${item} "#!/bin/sh" + puts ${item} "#" + puts ${item} "# MacPorts generated daemondo support script" + puts ${item} "#" + puts ${item} "" + + puts ${item} "#" + puts ${item} "# Init" + puts ${item} "#" + puts ${item} "prefix=$prefix" + foreach line [dict get $attrs init] { puts ${item} ${line} } + puts ${item} "" + + puts ${item} "#" + puts ${item} "# Start" + puts ${item} "#" + puts ${item} "Start()" + puts ${item} "\{" + foreach line [dict get $attrs start] { puts ${item} "\t${line}" } + puts ${item} "\}" + puts ${item} "" + + puts ${item} "#" + puts ${item} "# Stop" + puts ${item} "#" + puts ${item} "Stop()" + puts ${item} "\{" + foreach line [dict get $attrs stop] { puts ${item} "\t${line}" } + puts ${item} "\}" + puts ${item} "" + + puts ${item} "#" + puts ${item} "# Restart" + puts ${item} "#" + puts ${item} "Restart()" + puts ${item} "\{" + foreach line [dict get $attrs restart] { puts ${item} "\t${line}" } + puts ${item} "\}" + puts ${item} "" + + puts ${item} "#" + puts ${item} "# Run" + puts ${item} "#" + puts ${item} "Run()" + puts ${item} "\{" + puts ${item} "case \$1 in" + puts ${item} " start ) Start ;;" + puts ${item} " stop ) Stop ;;" + puts ${item} " restart) Restart ;;" + puts ${item} " * ) echo \"\$0: unknown argument: \$1\";;" + puts ${item} "esac" + puts ${item} "\}" + puts ${item} "" + + puts ${item} "#" + puts ${item} "# Run a phase based on the selector" + puts ${item} "#" + puts ${item} "Run \$1" + puts ${item} "" + + close ${item} + } + + if {[dict get $attrs netchange]} { + lappend args "--restart-netchange" + } + + # To log events then tell daemondo to log at verbosity=n + if {[dict get $attrs logevents]} { + lappend args "--verbosity=[option startupitem.daemondo.verbosity]" + } + + # If pidfile was specified, translate it for daemondo. + # + # There are four cases: + # (1) none + # (2) auto [pidfilename] + # (3) clean [pidfilename] + # (4) manual [pidfilename] + # + set pidfileArgCnt [llength [dict get $attrs pidfile]] + if {${pidfileArgCnt} > 0} { + if { $pidfileArgCnt == 1 } { + set pidFile "${prefix}/var/run/${itemname}.pid" + if {"${destroot}${prefix}/var/run" ni ${destroot.keepdirs}} { + lappend destroot.keepdirs "${destroot}${prefix}/var/run" + } + } else { + set pidFile [lindex [dict get $attrs pidfile] 1] + } + + if {${pidfileArgCnt} > 2} { + ui_error "$UI_PREFIX [msgcat::mc "Invalid parameter count to startupitem.pidfile: 2 expected, %d found" ${pidfileArgCnt}]" + } + + # Translate into appropriate arguments to daemondo + set pidStyle [lindex [dict get $attrs pidfile] 0] + switch -- ${pidStyle} { + none { lappend args "--pid=none" } + auto { lappend args "--pid=fileauto" "--pidfile" ${pidFile} } + clean { lappend args "--pid=fileclean" "--pidfile" ${pidFile} } + manual { lappend args "--pid=exec" "--pidfile" ${pidFile} } + default { + ui_error "$UI_PREFIX [msgcat::mc "Unknown pidfile style %s presented to startupitem.pidfile" ${pidStyle}]" + } + } + } else { + if {[dict get $attrs executable] ne ""} { + lappend args "--pid=exec" + } else { + lappend args "--pid=none" + } + } + + # Create the plist file + set plist [open "${destroot}${itemdir}/${plistname}" w 0644] + + puts ${plist} "" + puts ${plist} "" + puts ${plist} "" + puts ${plist} "" + + puts ${plist} "Label${uniquename}" + + puts ${plist} "ProgramArguments" + puts ${plist} "" + foreach arg ${args} { puts ${plist} "\t${arg}" } + puts ${plist} "" + + puts ${plist} "Disabled" + puts ${plist} "KeepAlive" + + if {$username ne ""} { + puts ${plist} "UserName$username" + } + + if {$groupname ne ""} { + puts ${plist} "GroupName$groupname" + } + + if {[dict get $attrs logfile] ne ""} { + puts ${plist} "StandardOutPath[dict get $attrs logfile]" + } + + if {[dict get $attrs logfile.stderr] ne ""} { + puts ${plist} "StandardErrorPath[dict get $attrs logfile.stderr]" + } + + if {[dict get $attrs debug]} { + puts ${plist} "Debug" + } + + puts ${plist} "" + puts ${plist} "" + + close ${plist} + + install_darwin_launchd ${itemdir}/${plistname} $daemondest [dict get $attrs install] +} + +proc startupitem_create {} { + global UI_PREFIX + + foreach_startupitem { + if {${si_type} ne "none" && ([tbool si_create] || $si_custom_file ne "")} { + if {[tbool si_create]} { + ui_debug "Creating ${si_type} control script '$si_name'" + } else { + ui_debug "Installing ${si_type} control script '$si_name'" + } + + switch -- ${si_type} { + launchd { startupitem_create_darwin_launchd $si_dict } + default { ui_error "$UI_PREFIX [msgcat::mc "Unrecognized startupitem type %s" ${si_type}]" } + } + } + } +} + +# Check if this port's startupitems are loaded +# Returns: list of loaded startupitems +proc loaded {} { + set launchctl_path ${::portutil::autoconf::launchctl_path} + if {$launchctl_path eq ""} { + # assuming not loaded if there's no launchctl + return [list] + } + set ret [list] + global os.major sudo_user + foreach_startupitem { + if {$si_type ne "launchd"} { + continue + } + if {$si_location eq "LaunchDaemons" && [getuid] == 0} { + set uid 0 + } elseif {[info exists sudo_user]} { + set uid [name_to_uid $sudo_user] + } else { + set uid [getuid] + } + if {${os.major} >= 14} { + if {$si_location eq "LaunchDaemons"} { + set domain system + } else { + set domain gui/${uid} + } + if {![catch {exec -ignorestderr $launchctl_path print ${domain}/${si_uniquename} >&/dev/null}]} { + lappend ret $si_name + } + } else { + if {![catch {exec_as_uid $uid {system "$launchctl_path list ${si_uniquename} > /dev/null"}}]} { + lappend ret $si_name + } + } + } + return $ret +} + +} diff --git a/src/port1.0/porttest.tcl b/src/port1.0/porttest.tcl index 62c5af69f..7ba0bbf2b 100644 --- a/src/port1.0/porttest.tcl +++ b/src/port1.0/porttest.tcl @@ -1,18 +1,12 @@ -# et:ts=4 -# porttest.tcl +# -*- 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 package provide porttest 1.0 -package require portutil 1.0 -package require portprogress 1.0 -package require machista 1.0 set org.macports.test [target_new org.macports.test porttest::test_main] target_provides ${org.macports.test} test target_requires ${org.macports.test} main fetch checksum extract patch configure build destroot target_prerun ${org.macports.test} porttest::test_start - -namespace eval porttest { -} +target_runpkg ${org.macports.test} porttest_run # define options options test.asroot test.ignore_archs test.run test.target @@ -25,106 +19,3 @@ default test.cmd {${build.cmd}} default test.pre_args {${test.target}} default test.target test default test.ignore_archs no - -set_ui_prefix - -proc porttest::get_file_archs {handle fpath} { - set resultlist [machista::parse_file $handle $fpath] - set returncode [lindex $resultlist 0] - set result [lindex $resultlist 1] - if {$returncode != $::machista::SUCCESS} { - # EMAGIC means not Mach-O (nor an archive of Mach-O objects); ignore - if {$returncode != $::machista::EMAGIC} { - ui_warn "Error parsing file ${fpath}: [machista::strerror $returncode]" - } - return "" - } - set ret [list] - set architecture [$result cget -mt_archs] - while {$architecture ne "NULL"} { - lappend ret [machista::get_arch_name [$architecture cget -mat_arch]] - set architecture [$architecture cget -next] - } - return $ret -} - -proc porttest::test_archs {} { - if {[option os.platform] ne "darwin" || [option test.ignore_archs]} { - return - } - set handle [machista::create_handle] - if {$handle eq "NULL"} { - error "Error creating libmachista handle" - } - set file_archs [dict create] - set destrootlen [string length [option destroot]] - - if {[getuid] == 0 && [geteuid] != 0} { - # file readable doesn't take euid into account - elevateToRoot test - set elevated 1 - } - - fs-traverse -depth fullpath [list [option destpath]] { - if {[file type $fullpath] ne "file"} { - continue - } - if {![file readable $fullpath]} { - ui_debug "Skipping unreadable file: $fullpath" - continue - } - if {[fileIsBinary $fullpath]} { - set archs [get_file_archs $handle $fullpath] - if {$archs ne ""} { - # not guaranteed to be listed in canonical order - dict lappend file_archs [lsort -ascii $archs] [string range $fullpath $destrootlen end] - } - } - } - - if {[info exists elevated]} { - dropPrivileges - } - - set wanted_archs [get_canonical_archs] - set has_wanted_archs [dict exists $file_archs $wanted_archs] - dict unset file_archs $wanted_archs - if {[dict size $file_archs] > 0} { - set msg "[option subport] is configured to build " - if {$wanted_archs eq "noarch"} { - append msg "no architecture-specific files," - } else { - append msg "for the architecture(s) '$wanted_archs'," - } - append msg " but installed Mach-O files built for the following archs:\n" - dict for {archs files} $file_archs { - append msg [join $archs ,]:\n - foreach f $files { - append msg " $f\n" - } - } - ui_warn $msg - } elseif {$wanted_archs ne "noarch" && !${has_wanted_archs}} { - ui_warn "[option subport] is configured to build for the architecture(s) '$wanted_archs',\ - but did not install any Mach-O files." - } - machista::destroy_handle $handle -} - -proc porttest::test_start {args} { - global UI_PREFIX subport - ui_notice "$UI_PREFIX [format [msgcat::mc "Testing %s"] ${subport}]" -} - -proc porttest::test_main {args} { - global test.run - - # built-in tests - porttest::test_archs - - # tests defined by the Portfile - if {[tbool test.run]} { - command_exec -callback portprogress::target_progress_callback test - } - return 0 -} diff --git a/src/port1.0/porttest_run.tcl b/src/port1.0/porttest_run.tcl new file mode 100644 index 000000000..a7ed3fc2b --- /dev/null +++ b/src/port1.0/porttest_run.tcl @@ -0,0 +1,110 @@ +# -*- 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 + +package provide porttest_run 1.0 +package require portprogress 1.0 +package require machista 1.0 + +namespace eval porttest { + +proc get_file_archs {handle fpath} { + set resultlist [machista::parse_file $handle $fpath] + set returncode [lindex $resultlist 0] + set result [lindex $resultlist 1] + if {$returncode != $::machista::SUCCESS} { + # EMAGIC means not Mach-O (nor an archive of Mach-O objects); ignore + if {$returncode != $::machista::EMAGIC} { + ui_warn "Error parsing file ${fpath}: [machista::strerror $returncode]" + } + return "" + } + set ret [list] + set architecture [$result cget -mt_archs] + while {$architecture ne "NULL"} { + lappend ret [machista::get_arch_name [$architecture cget -mat_arch]] + set architecture [$architecture cget -next] + } + return $ret +} + +proc test_archs {} { + if {[option os.platform] ne "darwin" || [option test.ignore_archs]} { + return + } + set handle [machista::create_handle] + if {$handle eq "NULL"} { + error "Error creating libmachista handle" + } + set file_archs [dict create] + set destrootlen [string length [option destroot]] + + if {[getuid] == 0 && [geteuid] != 0} { + # file readable doesn't take euid into account + elevateToRoot test + set elevated 1 + } + + fs-traverse -depth fullpath [list [option destpath]] { + if {[file type $fullpath] ne "file"} { + continue + } + if {![file readable $fullpath]} { + ui_debug "Skipping unreadable file: $fullpath" + continue + } + if {[fileIsBinary $fullpath]} { + set archs [get_file_archs $handle $fullpath] + if {$archs ne ""} { + # not guaranteed to be listed in canonical order + dict lappend file_archs [lsort -ascii $archs] [string range $fullpath $destrootlen end] + } + } + } + + if {[info exists elevated]} { + dropPrivileges + } + + set wanted_archs [get_canonical_archs] + set has_wanted_archs [dict exists $file_archs $wanted_archs] + dict unset file_archs $wanted_archs + if {[dict size $file_archs] > 0} { + set msg "[option subport] is configured to build " + if {$wanted_archs eq "noarch"} { + append msg "no architecture-specific files," + } else { + append msg "for the architecture(s) '$wanted_archs'," + } + append msg " but installed Mach-O files built for the following archs:\n" + dict for {archs files} $file_archs { + append msg [join $archs ,]:\n + foreach f $files { + append msg " $f\n" + } + } + ui_warn $msg + } elseif {$wanted_archs ne "noarch" && !${has_wanted_archs}} { + ui_warn "[option subport] is configured to build for the architecture(s) '$wanted_archs',\ + but did not install any Mach-O files." + } + machista::destroy_handle $handle +} + +proc test_start {args} { + global UI_PREFIX subport + ui_notice "$UI_PREFIX [format [msgcat::mc "Testing %s"] ${subport}]" +} + +proc test_main {args} { + global test.run + + # built-in tests + test_archs + + # tests defined by the Portfile + if {[tbool test.run]} { + command_exec -callback portprogress::target_progress_callback test + } + return 0 +} + +} diff --git a/src/port1.0/portuninstall.tcl b/src/port1.0/portuninstall.tcl index 3375ca99b..859a0f58b 100644 --- a/src/port1.0/portuninstall.tcl +++ b/src/port1.0/portuninstall.tcl @@ -1,38 +1,10 @@ # -*- 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 -# portuninstall.tcl -# -# Copyright (c) 2010-2011 The MacPorts Project -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of The MacPorts Project nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# + +# License: see portuninstall_run.tcl # the 'uninstall' target is provided by this package package provide portuninstall 1.0 -package require portutil 1.0 set org.macports.uninstall [target_new org.macports.uninstall portuninstall::uninstall_main] target_runtype ${org.macports.uninstall} always @@ -40,28 +12,7 @@ target_state ${org.macports.uninstall} no target_provides ${org.macports.uninstall} uninstall target_requires ${org.macports.uninstall} main target_prerun ${org.macports.uninstall} portuninstall::uninstall_start - -namespace eval portuninstall { -} +target_runpkg ${org.macports.uninstall} portuninstall_run options uninstall.asroot default uninstall.asroot no - -proc portuninstall::uninstall_start {args} { - global prefix ports_dryrun - if {(![file writable $prefix] && ![tbool ports_dryrun]) || ([getuid] == 0 && [geteuid] != 0)} { - # if install location is not writable, need root privileges - elevateToRoot "uninstall" - } -} - -proc portuninstall::uninstall_main {args} { - global subport _inregistry_version _inregistry_revision _inregistry_variants user_options - foreach {var backup} {_inregistry_version version _inregistry_revision revision _inregistry_variants portvariants} { - if {![info exists $var]} { - set $var [option $backup] - } - } - registry_uninstall $subport $_inregistry_version $_inregistry_revision $_inregistry_variants [array get user_options] - return 0 -} diff --git a/src/port1.0/portuninstall_run.tcl b/src/port1.0/portuninstall_run.tcl new file mode 100644 index 000000000..a27af37f3 --- /dev/null +++ b/src/port1.0/portuninstall_run.tcl @@ -0,0 +1,53 @@ +# -*- 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 +# +# Copyright (c) 2010-2011 The MacPorts Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The MacPorts Project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +package provide portuninstall_run 1.0 + +namespace eval portuninstall { + +proc uninstall_start {args} { + global prefix ports_dryrun + if {(![file writable $prefix] && ![tbool ports_dryrun]) || ([getuid] == 0 && [geteuid] != 0)} { + # if install location is not writable, need root privileges + elevateToRoot "uninstall" + } +} + +proc uninstall_main {args} { + global subport _inregistry_version _inregistry_revision _inregistry_variants user_options + foreach {var backup} {_inregistry_version version _inregistry_revision revision _inregistry_variants portvariants} { + if {![info exists $var]} { + set $var [option $backup] + } + } + registry_uninstall $subport $_inregistry_version $_inregistry_revision $_inregistry_variants [array get user_options] + return 0 +} + +} diff --git a/src/port1.0/portunload.tcl b/src/port1.0/portunload.tcl index 205f97e99..51643532a 100644 --- a/src/port1.0/portunload.tcl +++ b/src/port1.0/portunload.tcl @@ -1,90 +1,14 @@ -# et:ts=4 -# portunload.tcl -# -# Copyright (c) 2007, 2009, 2011 The MacPorts Project -# Copyright (c) 2007 James D. Berry -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of The MacPorts Project nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# +# -*- 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 + +# License: see portunload_run.tcl package provide portunload 1.0 -package require portutil 1.0 set org.macports.unload [target_new org.macports.unload portunload::unload_main] target_runtype ${org.macports.unload} always target_state ${org.macports.unload} no target_provides ${org.macports.unload} unload target_requires ${org.macports.unload} main - -namespace eval portunload { -} +target_runpkg ${org.macports.unload} portunload_run options unload.asroot - -set_ui_prefix - -proc portunload::unload_main {args} { - global UI_PREFIX prefix subport sudo_user \ - portutil::autoconf::launchctl_path - - portstartupitem::foreach_startupitem { - if {$si_install} { - set path /Library/${si_location}/${si_plist} - } else { - set path ${prefix}/etc/${si_location}/${si_plist} - } - if {$launchctl_path eq ""} { - return -code error [format [msgcat::mc "launchctl command was not found by configure"]] - } elseif {![file exists $path]} { - return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path] - } else { - set skip 0 - if {$si_location eq "LaunchDaemons"} { - if {[getuid] == 0} { - set uid 0 - } else { - ui_warn [format [msgcat::mc "Skipping unload of startupitem '%s' for %s, root privileges required"] $si_name $subport] - set skip 1 - } - } elseif {[getuid] == 0} { - if {[info exists sudo_user]} { - set uid [name_to_uid $sudo_user] - } else { - ui_warn [format [msgcat::mc "Unloading per-user startupitem '%s' for %s as root"] $si_name $subport] - set uid 0 - } - } else { - set uid [getuid] - } - if {!$skip} { - ui_notice "$UI_PREFIX [format [msgcat::mc "Unloading startupitem '%s' for %s"] $si_name $subport]" - exec_as_uid $uid {system "$launchctl_path unload -w $path"} - } - } - } - - return -} diff --git a/src/port1.0/portunload_run.tcl b/src/port1.0/portunload_run.tcl new file mode 100644 index 000000000..d70486e18 --- /dev/null +++ b/src/port1.0/portunload_run.tcl @@ -0,0 +1,78 @@ +# -*- 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 +# +# Copyright (c) 2007, 2009, 2011 The MacPorts Project +# Copyright (c) 2007 James D. Berry +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of The MacPorts Project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +package provide portunload_run 1.0 + +namespace eval portunload { + +proc unload_main {args} { + global UI_PREFIX prefix subport sudo_user \ + portutil::autoconf::launchctl_path + + portstartupitem::foreach_startupitem { + if {$si_install} { + set path /Library/${si_location}/${si_plist} + } else { + set path ${prefix}/etc/${si_location}/${si_plist} + } + if {$launchctl_path eq ""} { + return -code error [format [msgcat::mc "launchctl command was not found by configure"]] + } elseif {![file exists $path]} { + return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path] + } else { + set skip 0 + if {$si_location eq "LaunchDaemons"} { + if {[getuid] == 0} { + set uid 0 + } else { + ui_warn [format [msgcat::mc "Skipping unload of startupitem '%s' for %s, root privileges required"] $si_name $subport] + set skip 1 + } + } elseif {[getuid] == 0} { + if {[info exists sudo_user]} { + set uid [name_to_uid $sudo_user] + } else { + ui_warn [format [msgcat::mc "Unloading per-user startupitem '%s' for %s as root"] $si_name $subport] + set uid 0 + } + } else { + set uid [getuid] + } + if {!$skip} { + ui_notice "$UI_PREFIX [format [msgcat::mc "Unloading startupitem '%s' for %s"] $si_name $subport]" + exec_as_uid $uid {system "$launchctl_path unload -w $path"} + } + } + } + + return +} + +} diff --git a/src/port1.0/portutil.tcl b/src/port1.0/portutil.tcl index 38f39fa79..618ada491 100644 --- a/src/port1.0/portutil.tcl +++ b/src/port1.0/portutil.tcl @@ -36,7 +36,6 @@ package require Pextlib 1.0 package require macports_dlist 1.0 package require macports_util 1.0 package require msgcat -package require porttrace 1.0 namespace eval portutil { variable targets [list] @@ -1669,6 +1668,8 @@ proc recursive_collect_deps {portname {depsfound {}}} \ proc eval_targets {target} { + package require portsandbox + package require porttrace global portutil::targets subport version revision portvariants set dlist $targets diff --git a/src/port1.0/tests/portdistcheck.test b/src/port1.0/tests/portdistcheck.test index 12d8de915..59622c15a 100644 --- a/src/port1.0/tests/portdistcheck.test +++ b/src/port1.0/tests/portdistcheck.test @@ -8,6 +8,7 @@ set pwd [file dirname [file normalize $argv0]] source ../port_test_autoconf.tcl source $macports::autoconf::top_srcdir/src/macports1.0/tests/test_setup.tcl +package require port package require portutil 1.0 package require portdistcheck 1.0 package require portdistcheck_run 1.0 diff --git a/src/port1.0/tests/portinstall.test b/src/port1.0/tests/portinstall.test index 9ecfe3ddb..e309a72f3 100644 --- a/src/port1.0/tests/portinstall.test +++ b/src/port1.0/tests/portinstall.test @@ -8,6 +8,7 @@ set pwd [file dirname [file normalize $argv0]] source ../port_test_autoconf.tcl source $macports::autoconf::top_srcdir/src/macports1.0/tests/test_setup.tcl +package require portutil package require portinstall 1.0 source ../../registry2.0/portuninstall.tcl source ../port_autoconf.tcl diff --git a/src/port1.0/tests/portlint.test b/src/port1.0/tests/portlint.test index 9162c51d4..31a200133 100644 --- a/src/port1.0/tests/portlint.test +++ b/src/port1.0/tests/portlint.test @@ -8,7 +8,9 @@ set pwd [file dirname [file normalize $argv0]] source ../port_test_autoconf.tcl source $macports::autoconf::top_srcdir/src/macports1.0/tests/test_setup.tcl +package require portutil package require portlint 1.0 +package require portlint_run test verify_checksum_format_md5 { Test that we properly verify an MD5 checksum diff --git a/src/port1.0/tests/portlivecheck.test b/src/port1.0/tests/portlivecheck.test index d90281fb9..e5354c755 100644 --- a/src/port1.0/tests/portlivecheck.test +++ b/src/port1.0/tests/portlivecheck.test @@ -8,7 +8,9 @@ set pwd [file dirname [file normalize $argv0]] source ../port_test_autoconf.tcl source $macports::autoconf::top_srcdir/src/macports1.0/tests/test_setup.tcl +package require portutil package require portlivecheck 1.0 +package require portlivecheck_run macports_worker_init # --------------------------------------------------------------------------- diff --git a/src/port1.0/tests/portpatch.test b/src/port1.0/tests/portpatch.test index de7ba3b0c..4a3989a47 100644 --- a/src/port1.0/tests/portpatch.test +++ b/src/port1.0/tests/portpatch.test @@ -8,6 +8,7 @@ set pwd [file dirname [file normalize $argv0]] source ../port_test_autoconf.tcl source $macports::autoconf::top_srcdir/src/macports1.0/tests/test_setup.tcl +package require portutil package require portpatch source ../port_autoconf.tcl macports_worker_init diff --git a/src/port1.0/tests/portuninstall.test b/src/port1.0/tests/portuninstall.test index 18f1e1328..4d99699fa 100644 --- a/src/port1.0/tests/portuninstall.test +++ b/src/port1.0/tests/portuninstall.test @@ -8,6 +8,7 @@ set pwd [file dirname [file normalize $argv0]] source ../port_test_autoconf.tcl source $macports::autoconf::top_srcdir/src/macports1.0/tests/test_setup.tcl +package require portutil package require portinstall 1.0 source ../../registry2.0/portuninstall.tcl source ../port_autoconf.tcl