[SCM] deken/master: New upstream version 0.2.5
umlaeute at users.alioth.debian.org
umlaeute at users.alioth.debian.org
Tue Oct 24 09:50:18 UTC 2017
The following commit has been merged in the master branch:
commit d5d65fcab4eed9591abdfc289bd9acbf7923aee2
Author: IOhannes m zmölnig (Debian/GNU) <umlaeute at debian.org>
Date: Tue Oct 24 10:46:36 2017 +0200
New upstream version 0.2.5
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 428663d..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.*.swp
-virtualenv*
-build
-workspace
-deken.cfg
-.DS_Store
diff --git a/README.md b/README.md
index 896647f..06e887a 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,18 @@ so there is no need to manually install it.
Main development of the plugin is still happening in *this* repository,
so you might want to manually install the plugin to help testing new features.
-## Download ##
+When manually installing the `deken-plugin`, Pd will use it if (and only if) it has a greater version number
+than the one included in Pd.
+In this case you will see something like the following in the Pd-console (you first have to raise the verbosity to `Debug`):
+
+> `[deken]: installed version [0.2.1] < 0.2.3...overwriting!`
+> `deken-plugin.tcl (Pd externals search) in /home/frobnozzel/.local/lib/pd/extra/deken-plugin/ loaded.`
+
+## Download/Install ##
Click to download [deken-plugin.tcl](https://raw.githubusercontent.com/pure-data/deken/master/deken-plugin.tcl) and save it to your Pd folder:
- * Linux = `~/pd-externals/deken-plugin/`
+ * Linux = ~/.local/lib/pd/extra/deken-plugin/` (with Pd<0.47 try `~/pd-externals/deken-plugin/`)
* OSX = `~/Library/Pd/deken-plugin/`
* Windows = `%AppData%\Pd\deken-plugin\`
diff --git a/deken-extra-plugins/deken-xtra-apt-plugin.tcl b/deken-extra-plugins/deken-xtra-apt-plugin.tcl
index 372ddf1..d44eb3e 100644
--- a/deken-extra-plugins/deken-xtra-apt-plugin.tcl
+++ b/deken-extra-plugins/deken-xtra-apt-plugin.tcl
@@ -1,6 +1,6 @@
# META NAME PdExternalsSearch
-# META DESCRIPTION Search for externals zipfiles on puredata.info
-# META AUTHOR <Chris McCormick> chris at mccormick.cx
+# META DESCRIPTION Search for externals Debian-packages via apt
+# META AUTHOR IOhannes m zmölnig <zmoelnig at iem.at>
# ex: set setl sw=2 sts=2 et
# Search URL:
@@ -112,12 +112,7 @@ proc ::deken::apt::register { } {
if { [ catch { exec apt-cache madison } _ ] } { } {
if { [ catch { exec which grep-aptavail } _ ] } { } {
if { [ catch {
- ## oye a hack to get the apt-backend at the beginning of the backends
- if { [ info exists ::deken::backends ] } {
- set ::deken::backends [linsert $::deken::backends 0 ::deken::apt::search ]
- } {
- ::deken::register ::deken::apt::search
- }
+ ::deken::register ::deken::apt::search
} ] } {
::pdwindow::debug "Not using APT-backend for unavailable deken\n"
} {
diff --git a/deken-plugin.tcl b/deken-plugin.tcl
index 4fc4fdf..2fce00b 100644
--- a/deken-plugin.tcl
+++ b/deken-plugin.tcl
@@ -16,11 +16,27 @@ package require Tcl 8.4
# If a requirement is missing,
# Pd will load, but the script will not.
package require http 2
+# try enabling https if possible
+if { [catch {package require tls} ] } {} {
+ ::tls::init -ssl2 false -ssl3 false -tls1 true
+ ::http::register https 443 ::tls::socket
+}
+# try enabling PROXY support if possible
+if { [catch {package require autoproxy} ] } {} {
+ ::autoproxy::init
+ if { ! [catch {package present tls} stdout] } {
+ ::http::register https 443 ::autoproxy::tls_socket
+ }
+}
+
package require pdwindow 0.1
package require pd_menucommands 0.1
+package require pd_guiprefs
namespace eval ::deken:: {
variable version
+ variable installpath
+ variable protocol
}
## only register this plugin if there isn't any newer version already registered
@@ -31,16 +47,19 @@ proc ::deken::versioncheck {version} {
set v1 [split $version "."]
foreach x $v0 y $v1 {
if { $x > $y } {
- ::pdwindow::debug "\[deken\]: installed version \[$::deken::version\] > $version...skipping!\n"
+ ::pdwindow::debug [format [_ "\[deken\]: installed version \[%1\$s\] > %2\$s...skipping!" ] $::deken::version $version ]
+ ::pdwindow::debug "\n"
return 0
}
if { $x < $y } {
- ::pdwindow::debug "\[deken\]: installed version \[$::deken::version\] < $version...overwriting!\n"
+ ::pdwindow::debug [format [_ "\[deken\]: installed version \[%1\$s] < %2\$s...overwriting!" ] $::deken::version $version ]
+ ::pdwindow::debug "\n"
set ::deken::version $version
return 1
}
}
- ::pdwindow::debug "\[deken\]: installed version \[$::deken::version\] == $version...skipping!\n"
+ ::pdwindow::debug [format [_ "\[deken\]: installed version \[%1\$s\] == %2\$s...skipping!" ] $::deken::version $version ]
+ ::pdwindow::debug "\n"
return 0
}
set ::deken::version $version
@@ -48,7 +67,7 @@ proc ::deken::versioncheck {version} {
}
## put the current version of this package here:
-if { [::deken::versioncheck 0.2.1] } {
+if { [::deken::versioncheck 0.2.5] } {
namespace eval ::deken:: {
namespace export open_searchui
@@ -59,17 +78,35 @@ namespace eval ::deken:: {
variable statustext
variable statustimer
variable backends
+ variable progressvar
namespace export register
}
namespace eval ::deken::search:: { }
+set ::deken::installpath ""
set ::deken::statustimer ""
+set ::deken::protocol "http"
+if { ! [catch {package present tls} stdout] } {
+ set ::deken::protocol "https"
+}
+
+if { [ catch { set ::deken::installpath [::pd_guiprefs::read dekenpath] } stdout ] } {
+ # this is a Pd without the new GUI-prefs
+ proc ::deken::set_installpath {installdir} {
+ set ::deken::installpath $installdir
+ }
+} {
+ # Pd has a generic preferences system, that we can use
+ proc ::deken::set_installpath {installdir} {
+ set ::deken::installpath $installdir
+ ::pd_guiprefs::write dekenpath $installdir
+ }
+}
set ::deken::backends [list]
proc ::deken::register {fun} {
set ::deken::backends [linsert $::deken::backends 0 $fun]
}
-
proc ::deken::gettmpdir {} {
proc _iswdir {d} { expr [file isdirectory $d] * [file writable $d] }
set tmpdir ""
@@ -130,17 +167,20 @@ Set objShell = Nothing
}
set ::deken::_vbsunzip ""
+proc ::deken::get_tmpfilename {{path ""}} {
+ for {set i 0} {true} {incr i} {
+ set tmpfile [file join ${path} dekentmp.${i}]
+ if {![file exists $tmpfile]} {
+ return $tmpfile
+ }
+ }
+}
+
proc ::deken::get_writable_dir {paths} {
set fs [file separator]
set access [list RDWR CREAT EXCL TRUNC]
foreach p $paths {
- #if { [ catch { file mkdir $p } ] } {}
- for {set i 0} {True} {incr i} {
- set tmpfile "${p}${fs}dekentmp.${i}"
- if {![file exists $tmpfile]} {
- break
- }
- }
+ set tmpfile [::deken::get_tmpfilename $p]
# try creating tmpfile
if {![catch {open $tmpfile $access} channel]} {
close $channel
@@ -151,6 +191,28 @@ proc ::deken::get_writable_dir {paths} {
return
}
+# find an install path, either from prefs or on the system
+# returns an empty string if nothing was found
+proc ::deken::find_installpath {{ignoreprefs false}} {
+ set installpath ""
+ if { [ info exists ::deken::installpath ] && !$ignoreprefs } {
+ ## any previous choice?
+ set installpath [ ::deken::get_writable_dir [list $::deken::installpath ] ]
+ }
+ if { "$installpath" == "" } {
+ ## search the default paths
+ set installpath [ ::deken::get_writable_dir $::sys_staticpath ]
+ }
+ if { "$installpath" == "" } {
+ # let's use the first of $::sys_staticpath, if it does not exist yet
+ set userdir [lindex $::sys_staticpath 0]
+ if { [file exists ${userdir} ] } {} {
+ set installpath $userdir
+ }
+ }
+ return $installpath
+}
+
# list-reverter (compat for tcl<8.5)
if {[info command lreverse] == ""} {
proc lreverse list {
@@ -163,47 +225,6 @@ if {[info command lreverse] == ""} {
} ;# RS
}
-
-## where to look for the config-files:
-## - near the deken-plugin.tcl file
-## + at some user-specific place (e.g. ~/pd-externals/deken-plugin/)
-## it's probably easiest to iterate through curdir and ::sys_staticpath (in reverse order)
-## and read all (existing) configurations
-#
-## the configfile's format is simple:
-# the first element of a line is the variable name, the rest the value. e.g.
-# foo bar baz
-# will create a variable '::deken::foo' with value [list bar baz]
-## LATER: this is rather insecure, as it allows people to overwrite
-## virtually everything... (e.g. ::deken::search_for)
-proc ::deken::readconfig {paths filename} {
- proc doreadconfig {fname} {
- if {[file exists $fname]} {
- set fp [open $fname r]
- while {![eof $fp]} {
- set data [gets $fp]
- if { [string is list $data ] } {
- if { [llength $data ] > 1 } {
- set ::deken::[lindex $data 0] [lrange $data 1 end]
- }
- }
- }
- return True
- }
- return False
- }
- set fs [file separator]
- doreadconfig "$::current_plugin_loadpath${fs}${filename}"
- foreach p0 [lreverse $paths] {
- foreach p1 [ list "" "${fs}deken-plugin" ] {
- doreadconfig "${p0}${p1}${fs}${filename}"
- }
- }
-}
-
-
-::deken::readconfig $::sys_staticpath deken-plugin.conf
-
set ::deken::platform(os) $::tcl_platform(os)
set ::deken::platform(machine) $::tcl_platform(machine)
set ::deken::platform(bits) [ expr [ string length [ format %X -1 ] ] * 4 ]
@@ -224,14 +245,16 @@ if { "Windows" eq "$::deken::platform(os)" } {
# console message to let them know we're loaded
## but only if we are being called as a plugin (not as built-in)
if { "" != "$::current_plugin_loadpath" } {
- ::pdwindow::post "deken-plugin.tcl (Pd externals search) in $::current_plugin_loadpath loaded.\n"
- ::pdwindow::post "Platform detected: $::deken::platform(os)-$::deken::platform(machine)-$::deken::platform(bits)bit\n"
+ ::pdwindow::post [format [_ "\[deken\] deken-plugin.tcl (Pd externals search) loaded from %s." ] $::current_plugin_loadpath ]
+ ::pdwindow::post "\n"
}
+::pdwindow::verbose 0 [format [_ "\[deken\] Platform detected: %s" ] $::deken::platform(os)-$::deken::platform(machine)-$::deken::platform(bits)bit ]
+::pdwindow::verbose 0 "\n"
# architectures that can be substituted for eachother
array set ::deken::architecture_substitutes {}
-set ::deken::architecture_substitutes(x86_64) [list "amd64" "i386" "i586" "i686"]
-set ::deken::architecture_substitutes(amd64) [list "x86_64" "i386" "i586" "i686"]
+set ::deken::architecture_substitutes(x86_64) [list "amd64" ]
+set ::deken::architecture_substitutes(amd64) [list "x86_64" ]
set ::deken::architecture_substitutes(i686) [list "i586" "i386"]
set ::deken::architecture_substitutes(i586) [list "i386"]
set ::deken::architecture_substitutes(armv6l) [list "armv6" "arm"]
@@ -239,7 +262,24 @@ set ::deken::architecture_substitutes(armv7l) [list "armv7" "armv6l" "armv6" "ar
set ::deken::architecture_substitutes(PowerPC) [list "ppc"]
set ::deken::architecture_substitutes(ppc) [list "PowerPC"]
-proc ::deken::status {msg} {
+# try to set install path when plugin is loaded
+set ::deken::installpath [::deken::find_installpath]
+
+# allow overriding deken platform from Pd-core
+proc ::deken::set_platform {os machine bits} {
+ if { $os != $::deken::platform(os) ||
+ $machine != $::deken::platform(machine) ||
+ $bits != $::deken::platform(bits)} {
+ set ::deken::platform(os) ${os}
+ set ::deken::platform(machine) ${machine}
+ set ::deken::platform(bits) ${bits}
+
+ ::pdwindow::verbose 0 [format [_ "\[deken\] Platform (re)detected: %s" ] ${os}-${machine}-${bits}bit ]
+ ::pdwindow::verbose 0 "\n"
+ }
+}
+
+proc ::deken::status {{msg ""}} {
#variable mytoplevelref
#$mytoplevelref.results insert end "$msg\n"
#$mytoplevelref.status.label -text "$msg"
@@ -278,9 +318,10 @@ proc ::deken::highlightable_posttag {tag} {
$mytoplevelref.results tag raise highlight
}
proc ::deken::prompt_installdir {} {
- set installdir [tk_chooseDirectory -title "Install libraries to directory:"]
+ set installdir [tk_chooseDirectory -title [_ "Install externals to directory:"] \
+ -initialdir $::fileopendir -parent .externals_searchui]
if { "$installdir" != "" } {
- set ::deken::installpath $installdir
+ ::deken::set_installpath $installdir
return 1
}
return 0
@@ -295,6 +336,10 @@ proc ::deken::update_searchbutton {mytoplevel} {
}
}
+proc ::deken::progress {x} {
+ ::deken::post "= ${x}%"
+}
+
# this function gets called when the menu is clicked
proc ::deken::open_searchui {mytoplevel} {
if {[winfo exists $mytoplevel]} {
@@ -309,7 +354,7 @@ proc ::deken::open_searchui {mytoplevel} {
$mytoplevel.results tag configure archmatch
$mytoplevel.results tag configure noarchmatch -foreground grey
}
- ::deken::post "To get a list of all available externals, try an empty search." info
+ ::deken::post [_ "To get a list of all available externals, try an empty search."] info
}
# build the externals search dialog window
@@ -334,12 +379,12 @@ proc ::deken::create_dialog {mytoplevel} {
bind $mytoplevel.searchbit.entry <Key-Return> "::deken::initiate_search $mytoplevel"
bind $mytoplevel.searchbit.entry <KeyRelease> "::deken::update_searchbutton $mytoplevel"
focus $mytoplevel.searchbit.entry
- button $mytoplevel.searchbit.button -text [_ "Show all"] -default active -width 9 -command "::deken::initiate_search $mytoplevel"
- pack $mytoplevel.searchbit.button -side right -padx 6 -pady 3
+ button $mytoplevel.searchbit.button -text [_ "Show all"] -default active -command "::deken::initiate_search $mytoplevel"
+ pack $mytoplevel.searchbit.button -side right -padx 6 -pady 3 -ipadx 10
frame $mytoplevel.warning
pack $mytoplevel.warning -side top -fill x
- label $mytoplevel.warning.label -text "Only install externals uploaded by people you trust."
+ label $mytoplevel.warning.label -text [_ "Only install externals uploaded by people you trust."]
pack $mytoplevel.warning.label -side left -padx 6
frame $mytoplevel.status
@@ -350,19 +395,30 @@ proc ::deken::create_dialog {mytoplevel} {
text $mytoplevel.results -takefocus 0 -cursor hand2 -height 100 -yscrollcommand "$mytoplevel.results.ys set"
scrollbar $mytoplevel.results.ys -orient vertical -command "$mytoplevel.results yview"
pack $mytoplevel.results.ys -side right -fill y
- pack $mytoplevel.results -side left -padx 6 -pady 3 -fill both -expand true
+ pack $mytoplevel.results -side top -padx 6 -pady 3 -fill both -expand true
+
+ if { [ catch {
+ ttk::progressbar $mytoplevel.progress -orient horizontal -length 640 -maximum 100 -mode determinate -variable ::deken::progressvar } stdout ] } {
+ } {
+ pack $mytoplevel.progress -side bottom
+ proc ::deken::progress {x} {
+ set ::deken::progressvar $x
+ }
+ }
}
proc ::deken::initiate_search {mytoplevel} {
# let the user know what we're doing
::deken::clearpost
- ::deken::post "Searching for externals..."
+ ::deken::post [_ "Searching for externals..."]
+ set ::deken::progressvar 0
# make the ajax call
if { [ catch {
set results [::deken::search_for [$mytoplevel.searchbit.entry get]]
} stdout ] } {
- ::pdwindow::debug "\[deken\]: online? $stdout\n"
- ::deken::status "Unable to perform search. Are you online?"
+ ::pdwindow::debug [format [_ "\[deken\]: online? %s" ] $stdout ]
+ ::pdwindow::debug "\n"
+ ::deken::status [format "%s %s" [_ "Unable to perform search." ] [_ "Are you online?" ] ]
} else {
# delete all text in the results
::deken::clearpost
@@ -379,24 +435,24 @@ proc ::deken::initiate_search {mytoplevel} {
}
::deken::scrollup
} else {
- ::deken::post "No matching externals found. Try using the full name e.g. 'freeverb'."
+ ::deken::post [_ "No matching externals found. Try using the full name e.g. 'freeverb'." ]
}
}}
# display a single found entry
proc ::deken::show_result {mytoplevel counter result showmatches} {
- foreach {title cmd match comment status} $result {break}
+ foreach {title cmd match comment status} $result {break}
- set tag ch$counter
+ set tag ch$counter
#if { [ ($match) ] } { set matchtag archmatch } { set matchtag noarchmatch }
- set matchtag [expr $match?"archmatch":"noarchmatch" ]
- if {($match == $showmatches)} {
- set comment [string map {"\n" "\n\t"} $comment]
- ::deken::post "$title\n\t$comment\n" [list $tag $matchtag]
- ::deken::highlightable_posttag $tag
- ::deken::bind_posttag $tag <Enter> "+::deken::status $status"
- ::deken::bind_posttag $tag <1> "$cmd"
- }
+ set matchtag [expr $match?"archmatch":"noarchmatch" ]
+ if {($match == $showmatches)} {
+ set comment [string map {"\n" "\n\t"} $comment]
+ ::deken::post "$title\n\t$comment\n" [list $tag $matchtag]
+ ::deken::highlightable_posttag $tag
+ ::deken::bind_posttag $tag <Enter> "+::deken::status {$status}"
+ ::deken::bind_posttag $tag <1> "$cmd"
+ }
}
# handle a clicked link
@@ -405,41 +461,81 @@ proc ::deken::clicked_link {URL filename} {
### if ::deken::installpath is set, use the first writable item
### if not, get a writable item from one of the searchpaths
### if this still doesn't help, ask the user
- set installdir ""
- if { [ info exists ::deken::installpath ] } {
- ## any previous choice?
- set installdir [ ::deken::get_writable_dir [list $::deken::installpath ] ]
- }
- if { "$installdir" == "" } {
- ## search the default paths
- set installdir [ ::deken::get_writable_dir $::sys_staticpath ]
- }
+ set installdir [::deken::find_installpath]
+ set extname [lindex [split $filename "-"] 0]
if { "$installdir" == "" } {
- ## ask the user (and remember the decision)
- ::deken::prompt_installdir
- set installdir [ ::deken::get_writable_dir [list $::deken::installpath ] ]
+ if {[namespace exists ::pd_docsdir] && [::pd_docsdir::externals_path_is_valid]} {
+ # if the docspath is set, try the externals subdir
+ set installdir [::pd_docsdir::get_externals_path]
+ } else {
+ # ask the user (and remember the decision)
+ ::deken::prompt_installdir
+ set installdir [ ::deken::get_writable_dir [list $::deken::installpath ] ]
+ }
}
- if { "$installdir" == "" } {
- #::deken::clearpost
- ::deken::post "No writeable directory found in:" warn
- foreach p $::sys_staticpath { ::deken::post "\t- $p" warn }
- ::deken::post "Cannot download/install libraries!" warn
- return
+ while {1} {
+ if { "$installdir" == "" } {
+ set msg [_ "Please select a (writable) installation directory!"]
+ set _args "-message $msg -type retrycancel -default retry -icon warning -parent .externals_searchui"
+ switch -- [eval tk_messageBox ${_args}] {
+ cancel {return}
+ retry {
+ if {[::deken::prompt_installdir]} {
+ set installdir $::deken::installpath
+ } else {
+ continue
+ }
+ }
+ }
+ } else {
+ set msg [_ "Install %s to %s?" ]
+ set _args "-message \"[format $msg $extname $installdir]\" -type yesnocancel -default yes -icon question -parent .externals_searchui"
+ switch -- [eval tk_messageBox ${_args}] {
+ cancel {return}
+ yes { }
+ no {
+ set prevpath $::deken::installpath
+ if {[::deken::prompt_installdir]} {
+ set keepprevpath 1
+ set installdir $::deken::installpath
+ # if docsdir is set & the install path is valid,
+ # saying "no" is temporary to ensure the docsdir
+ # hierarchy remains, use the Path dialog to override
+ if {[namespace exists ::pd_docsdir] && [::pd_docsdir::path_is_valid] &&
+ [file writable [file normalize $prevpath]] } {
+ set keepprevpath 0
+ }
+ if {$keepprevpath} {
+ set ::deken::installpath $prevpath
+ }
+ } else {
+ continue
+ }
+ }
+ }
+ }
+
+ if { "$installdir" != "" } {
+ # try creating the installdir...just in case
+ if { [ catch { file mkdir $installdir } ] } {}
+ }
+ # check whether this is a writable directory
+ set installdir [ ::deken::get_writable_dir [list $installdir ] ]
+ if { "$installdir" != "" } {
+ # stop looping if we've found our dir
+ break
+ }
}
- switch -- [tk_messageBox -message \
- "Install to directory $installdir?" \
- -type yesnocancel -default "yes" \
- -icon question] {
- no {set installdir ""
- if {[::deken::prompt_installdir]} {
- set installdir [ ::deken::get_writable_dir [list $::deken::installpath ] ] }
- if { "$installdir" eq "" } return}
- cancel return}
set fullpkgfile "$installdir/$filename"
::deken::clearpost
- ::deken::post "Commencing downloading of:\n$URL\nInto $installdir..."
- ::deken::download_file $URL $fullpkgfile
+ ::deken::post [format [_ "Commencing downloading of:\n%1\$s\nInto %2\$s..."] $URL $installdir] ]
+ set fullpkgfile [::deken::download_file $URL $fullpkgfile]
+ if { "$fullpkgfile" eq "" } {
+ ::deken::post [_ "aborting."] info
+ return
+ }
+
set PWD [ pwd ]
cd $installdir
set success 1
@@ -450,52 +546,102 @@ proc ::deken::clicked_link {URL filename} {
set success 0
}
}
- } elseif { [ string match *.tar.gz $fullpkgfile ]
+ } elseif { [ string match *.tar.* $fullpkgfile ]
|| [ string match *.tgz $fullpkgfile ]
} then {
- if { [ catch { exec tar xzf $fullpkgfile } stdout ] } {
+ if { [ catch { exec tar xf $fullpkgfile } stdout ] } {
::pdwindow::debug "$stdout\n"
set success 0
}
}
cd $PWD
if { $success > 0 } {
- ::deken::post "Successfully unzipped $filename into $installdir.\n"
- catch { exec rm $fullpkgfile }
+ ::deken::post [format [_ "Successfully unzipped %1\$s into %2\$s."] $filename $installdir ]
+ ::deken::post ""
+ catch { file delete $fullpkgfile }
} else {
# Open both the fullpkgfile folder and the zipfile itself
# NOTE: in tcl 8.6 it should be possible to use the zlib interface to actually do the unzip
- ::deken::post "Unable to extract package automatically." warn
- ::deken::post "Please perform the following steps manually:"
- ::deken::post "1. Unzip $fullpkgfile."
+ ::deken::post [_ "Unable to extract package automatically." ] warn
+ ::deken::post [_ "Please perform the following steps manually:" ]
+ ::deken::post [format [_ "1. Unzip %s." ] $fullpkgfile ]
pd_menucommands::menu_openfile $fullpkgfile
- ::deken::post "2. Copy the contents into $installdir.\n"
+ ::deken::post [format [_ "2. Copy the contents into %s." ] $installdir]
+ ::deken::post ""
pd_menucommands::menu_openfile $installdir
}
+
+## FIXXME: should we really suggest to add to search-paths?
+## per library search-paths should be added via [declare]
+
+ # add to the search paths? bail if the version of pd doesn't support it
+ if {[uplevel 1 info procs add_to_searchpaths] eq ""} {return}
+ set extpath [file join $installdir $extname]
+ if {![file exists $extpath]} {
+ ::deken::post [format [_ "Unable to add %s to search paths"] $extname]
+ return
+ }
+ set msg [_ "Add %s to the Pd search paths?" ]
+ set _args "-message \"[format $msg $extname]\" -type yesno -default yes -icon question -parent .externals_searchui"
+ switch -- [eval tk_messageBox ${_args}] {
+ yes {
+ add_to_searchpaths [file join $installdir $extname]
+ ::deken::post [format [_ "Added %s to search paths"] $extname]
+ # if this version of pd supports it, try refreshing the helpbrowser
+ if {[uplevel 1 info procs ::helpbrowser::refresh] ne ""} {
+ ::helpbrowser::refresh
+ }
+ }
+ no {
+ return
+ }
+ }
}
# download a file to a location
# http://wiki.tcl.tk/15303
proc ::deken::download_file {URL outputfilename} {
- set f [open $outputfilename w]
- set status ""
- set errorstatus ""
+ set downloadfilename [::deken::get_tmpfilename [file dirname $outputfilename] ]
+ set f [open $downloadfilename w]
fconfigure $f -translation binary
- set httpresult [http::geturl $URL -binary true -progress "::deken::download_progress" -channel $f]
- set status [::http::status $httpresult]
- set errorstatus [::http::error $httpresult]
+
+ set httpresult [::http::geturl $URL -binary true -progress "::deken::download_progress" -channel $f]
+ set ncode [::http::ncode $httpresult]
+ if {[expr $ncode != 200 ]} {
+ ## FIXXME: we probably should handle redirects correctly
+ # tcl-format
+ set err [::http::code $token]
+ ::deken::post [format [_ "Unable to download from %1\$s \[%2\$s\]" ] $URL $err ] error
+ set outputfilename ""
+ }
flush $f
close $f
- http::cleanup $httpresult
- return [list $status $errorstatus ]
+ ::http::cleanup $httpresult
+
+ if { "$outputfilename" != "" } {
+ catch { file delete $outputfilename }
+ if {[file exists $outputfilename]} {
+ ::deken::post [format [_ "Unable to remove stray file %s" ] $outputfilename ] error
+ set outputfilename ""
+ }
+ }
+ if { $outputfilename != "" && "$outputfilename" != "$downloadfilename" } {
+ if {[catch { file rename $downloadfilename $outputfilename}]} {
+ ::deken::post [format [_ "Unable to rename downloaded file to %s" ] $outputfilename ] error
+ set outputfilename ""
+ }
+ }
+ if { "$outputfilename" eq "" } {
+ file delete $downloadfilename
+ }
+
+ return $outputfilename
}
# print the download progress to the results window
proc ::deken::download_progress {token total current} {
if { $total > 0 } {
- variable mytoplevelref
- set computed [expr {round(100 * (1.0 * $current / $total))}]
- ::deken::post "= $computed%"
+ ::deken::progress [expr {round(100 * (1.0 * $current / $total))}]
}
}
@@ -531,10 +677,8 @@ proc ::deken::architecture_match {archs} {
# check each architecture in our list against the current one
foreach arch $archs {
if { [ regexp -- {(.*)-(.*)-(.*)} $arch _ os machine bits ] } {
- if { "${os}" eq "$::deken::platform(os)" &&
- "${bits}" eq "$::deken::platform(bits)"
- } {
- ## so OS and word size match
+ if { "${os}" eq "$::deken::platform(os)" } {
+ ## so OS matches
## check whether the CPU matches as well
if { "${machine}" eq "$::deken::platform(machine)" } {return 1}
## not exactly; see whether it is in the list of compat CPUs
@@ -550,7 +694,7 @@ proc ::deken::architecture_match {archs} {
}
proc ::deken::search_for {term} {
- ::deken::status "searching for '$term'"
+ ::deken::status [format [_ "searching for '%s'" ] $term ]
set result [list]
foreach searcher $::deken::backends {
@@ -564,6 +708,7 @@ set mymenu .menubar.help
if { [catch {
$mymenu entryconfigure [_ "Find externals"] -command {::deken::open_searchui .externals_searchui}
} _ ] } {
+ $mymenu add separator
$mymenu add command -label [_ "Find externals"] -command {::deken::open_searchui .externals_searchui}
}
# bind all <$::modifier-Key-s> {::deken::open_helpbrowser .helpbrowser2}
@@ -620,10 +765,33 @@ proc urldecode {str} {
## searching puredata.info
proc ::deken::search::puredata.info {term} {
set searchresults [list]
+ set dekenserver "${::deken::protocol}://deken.puredata.info/search"
+ catch {set dekenserver $::env(DEKENSERVER)} stdout
+ set queryterm {}
+ foreach x $term {lappend queryterm name $x}
+ if { [ catch {set queryterm [::http::formatQuery {*}$queryterm ] } stderr ] } {
+ set queryterm [ join $term "&name=" ]
+ set queryterm "name=${queryterm}"
+ }
+
+ # deken-specific socket config
+ set httpaccept [::http::config -accept]
+ ::http::config -accept text/tab-separated-values
- set term [ join $term "&name=" ]
- set token [http::geturl "http://deken.puredata.info/search?name=$term"]
- set contents [http::data $token]
+ # fetch search result
+ set token [::http::geturl "${dekenserver}?${queryterm}"]
+
+ # restore http settings
+ ::http::config -accept $httpaccept
+
+ set ncode [::http::ncode $token]
+ if {[expr $ncode != 200 ]} {
+ set err [::http::code $token]
+ ::pdwindow::debug [format "\[deken\]: %s %s" [_ "Unable to perform search." ] ${err} ]
+ ::pdwindow::debug "\n"
+ return {}
+ }
+ set contents [::http::data $token]
set splitCont [split $contents "\n"]
# loop through the resulting tab-delimited table
foreach ele $splitCont {
@@ -643,14 +811,14 @@ proc ::deken::search::puredata.info {term} {
set match [::deken::architecture_match "$archs" ]
- set comment "Uploaded by $creator @ $date"
+ set comment [format [_ "Uploaded by %1\$s @ %2\$s" ] $creator $date ]
set status $URL
set sortname [lindex $pkgverarch 0]--[lindex $pkgverarch 1]--$date
set res [list $name $cmd $match $comment $status $filename]
lappend searchresults $res
}
}
- http::cleanup $token
+ ::http::cleanup $token
return [lsort -dictionary -decreasing -index 5 $searchresults ]
}
diff --git a/developer/README.md b/developer/README.md
index 8a33247..0b07ca0 100644
--- a/developer/README.md
+++ b/developer/README.md
@@ -43,13 +43,13 @@ with the zip file.
### Creating/Uploading packages on a different machine
`deken` inspects the files in the directory to determine the target platform
-(rather than just checking on which system you are currently runing).
+(rather than just checking on which system you are currently running).
Therefore, if it is not feasible to install `deken` on the machine used for
building your Pd library, you can run `deken` on another machine,
Example: You build the "my_external" library on OSX-10.5, but (due to OSX-10.5
not being supported by Apple anymore) you haven't installed `deken` there.
-So you simply transfer the "my_external" directory to your linux machine, where
+So you simply transfer the "my_external" directory to your Linux machine, where
you run `deken package my_external` and it will magically create the
`my_external-v3.14-(Darwin-i386-32)(Darwin-x86_64-32)-externals.tgz` file for
you, ready to be uploaded.
@@ -69,9 +69,9 @@ be optimally searchable on [puredata.info](http://puredata.info/);
this archive).
It is either "Sources" (see [below](#sourceful-uploads) or `OS-MARCH-BIT`,
with:
- - OS being the Operating System (`Linux`, `Darwin`, W32`,...)
+ - OS being the Operating System (`Linux`, `Darwin`, `W32`,...)
- MARCH is the machine architecture (e.g. `x86_64`)
- - BIT is some number of bits (e.g. `64`)
+ - BIT is some number of bits (e.g. `32`)
* EXT is the archive extension (either `zip` or `tar.gz`)
Note that the archive should contain a single directory at the top level with
@@ -85,10 +85,10 @@ optional, don't include them. The same goes for the curly braces around the
However, the round parentheses "()" around architectures must be included to
separate the architectures visibly from each other.
-In plain english this means:
+In plain English this means:
> the library-name, followed by an optional version string (starting with `-v`
> and ending with `-`), followed by zero or more architecture specifications
-> (each surrounded by `(`parantheses`)`), and terminated by `-externals`
+> (each surrounded by `(`parentheses`)`), and terminated by `-externals`
> (followed by a filename extension).
diff --git a/developer/builder.md b/developer/builder.md
deleted file mode 100644
index 38c2694..0000000
--- a/developer/builder.md
+++ /dev/null
@@ -1,35 +0,0 @@
-Deken can also be used to build some externals directly from a repository.
-
-## Build an external from a repository ##
-
- $ deken build svn://svn.code.sf.net/p/pure-data/svn/trunk/externals/freeverb~/
- Deken 0.1
- Checking out svn://svn.code.sf.net/p/pure-data/svn/trunk/externals/freeverb~/ into ./workspace/externals/freeverb~
- Building ./workspace/externals/freeverb~
-
-## Build and install an external from a repository ##
-
- $ deken install svn://svn.code.sf.net/p/pure-data/svn/trunk/externals/freeverb~/
- Deken 0.1
- Updating ./workspace/externals/freeverb~
- Building ./workspace/externals/freeverb~
- Installing ./workspace/externals/freeverb~ into ./pd-externals/freeverb~
-
-## Manage Pd version ##
-
-Show Pd version:
-
- $ deken pd
- Deken 0.1
- Pd version 0.43 checked out
-
-Change Pd version:
-
- $ deken pd master
- Deken 0.1
- Pd version master checked out
-
-### How to make your externals compatible ###
-
-<http://puredata.info/docs/developer/MakefileTemplate>
-
diff --git a/developer/deken b/developer/deken
index 02aa554..123cb66 100755
--- a/developer/deken
+++ b/developer/deken
@@ -7,11 +7,21 @@
# Much of this code is pilfered from Clojure's Leiningen tool
export DEKEN_VERSION="0.1"
+export DEKEN_HOME="${DEKEN_HOME:-"$HOME/.deken"}"
+DEKEN_BASE_URL="https://raw.githubusercontent.com/pure-data/deken/master/developer"
+
+VIRTUALENV_VERSION="15.1.0"
+VIRTUALENV_URL="https://pypi.io/packages/source/v/virtualenv/virtualenv-${VIRTUALENV_VERSION}.tar.gz"
+
+
+error() {
+ echo "$@" 1>&2
+}
if [ $(id -u) -eq 0 ] && [ "$DEKEN_ROOT" = "" ]; then
- echo "WARNING: You're currently running as root; probably by accident."
- echo "Press control-C to abort or Enter to continue as root."
- echo "Set DEKEN_ROOT to disable this warning."
+ error "WARNING: You're currently running as root; probably by accident."
+ error "Press control-C to abort or Enter to continue as root."
+ error "Set DEKEN_ROOT to disable this warning."
read _
fi
@@ -27,6 +37,11 @@ else
cygwin=false
fi
+# allow the user to override the default python
+if [ "x${PYTHON_BIN}" = x ]; then
+ PYTHON_BIN=python
+fi
+
# This needs to be defined before we call HTTP_CLIENT below
if [ "$HTTP_CLIENT" = "" ]; then
if which curl >/dev/null; then
@@ -39,17 +54,11 @@ if [ "$HTTP_CLIENT" = "" ]; then
fi
fi
-export DEKEN_HOME="${DEKEN_HOME:-"$HOME/.deken"}"
-DEKEN_BASE_URL="https://raw.githubusercontent.com/pure-data/deken/master/developer"
if $cygwin; then
export DEKEN_HOME=$(cygpath -w "$DEKEN_HOME")
fi
-error() {
- echo "$@" 1>&2
-}
-
bail_install() {
error "Self-installation of Deken failed."
error "Please paste any errors in the bug tracker at https://github.com/pure-data/deken/issues"
@@ -63,26 +72,40 @@ bail_install_msg() {
bail_install
}
+bail_requirements() {
+ rm -f "${DEKEN_HOME}/requirements.txt"
+ error "Installation of requirements failed."
+ error "You probably should install the following packages first:"
+ error " - 'python-dev'"
+ error " - 'libffi-dev'"
+ error " - 'libssl-dev'"
+ error "You can run 'deken install' or 'deken upgrade' anytime to"
+ error " re-install (or upgrade) your Deken installation"
+
+ exit 1
+}
+
install_virtualenv() {
echo "Downloading & installing Virtualenv."
rm -rf $DEKEN_HOME/virtualenv-source
mkdir -p $DEKEN_HOME/virtualenv-source && \
- $HTTP_CLIENT $DEKEN_HOME/virtualenv.tar.gz https://pypi.python.org/packages/source/v/virtualenv/virtualenv-12.1.1.tar.gz && \
+ $HTTP_CLIENT $DEKEN_HOME/virtualenv.tar.gz "${VIRTUALENV_URL}" && \
tar -zxvf $DEKEN_HOME/virtualenv.tar.gz -C $DEKEN_HOME/virtualenv-source/ && \
mv $DEKEN_HOME/virtualenv-source/virtualenv-*/* $DEKEN_HOME/virtualenv-source
[ -d "$DEKEN_HOME/virtualenv-source" ] && (\
cd $DEKEN_HOME/virtualenv-source && \
- /usr/bin/env python setup.py build ) \
+ /usr/bin/env "${PYTHON_BIN}" setup.py build ) \
|| bail_install;
}
install_deken() {
- which python >/dev/null || \
- bail_install_msg "Oops, no Python found! You need Python to run Deken."
+ which "${PYTHON_BIN}" >/dev/null || \
+ bail_install_msg "Oops, no Python found! You need Python to run Deken: ${PYTHON_BIN}
+You can specify an alternative Python interpreter via the PYTHON_BIN envvar"
which make >/dev/null || \
bail_install_msg "Oops, no Make found! You need Make to run Deken."
error "This is your first time running deken on this machine."
- error "I'm going to install myself and my dependencies into ~/.deken now."
+ error "I'm going to install myself and my dependencies into ${DEKEN_HOME} now."
error "Feel free to ctrl-C now if you don't want to do this."
sleep 3;
[ -d "$DEKEN_HOME" ] || mkdir -p $DEKEN_HOME;
@@ -100,7 +123,7 @@ install_deken() {
$DEKEN_HOME/virtualenv-source/virtualenv.py "$DEKEN_HOME/virtualenv" || exit 1)
[ -x "$DEKEN_HOME/virtualenv/bin/hy" ] || (\
echo "Installing deken library dependencies." && \
- $DEKEN_HOME/virtualenv/bin/pip install -r $DEKEN_HOME/requirements.txt || exit 1)
+ $DEKEN_HOME/virtualenv/bin/pip install -r $DEKEN_HOME/requirements.txt || bail_requirements)
}
upgrade_deken() {
@@ -115,10 +138,25 @@ upgrade_deken() {
mv $DEKEN_HOME/.upgrade-$f $DEKEN_HOME/$f;
done
# finally update the python dependencies
- $DEKEN_HOME/virtualenv/bin/pip install -r $DEKEN_HOME/requirements.txt
+ $DEKEN_HOME/virtualenv/bin/pip install -r $DEKEN_HOME/requirements.txt || bail_requirements
echo "Successfully upgraded."
}
+tryrun_deken() {
+ if [ ! -x "$DEKEN_HOME/virtualenv/bin/hy" ]; then
+ error "Unable to find '${DEKEN_HOME}/virtualenv/bin/hy'"
+ error "Try running '$0 install' or '$0 upgrade'"
+ exit 1
+ fi
+ if [ -e $DEKEN_HOME/deken.hy ]; then
+ $DEKEN_HOME/virtualenv/bin/hy $DEKEN_HOME/deken.hy "$@"
+ else
+ error "Unable to find '${DEKEN_HOME}/deken.hy'"
+ error "Try running '$0 install' or '$0 upgrade'"
+ exit 1
+ fi
+}
+
# make sure we are deployed
[ -d "$DEKEN_HOME" ] || install_deken
@@ -126,8 +164,16 @@ upgrade_deken() {
[ -d "$DEKEN_HOME" ] || bail_install;
# catch the special "upgrade" command
-[ "$1" = "upgrade" ] && \
- # run he upgrade command instead
- upgrade_deken || \
+case "$1" in
+ upgrade|update)
+ # run the upgrade command instead
+ upgrade_deken
+ ;;
+ install)
+ install_deken
+ ;;
+ *)
# run the real deken command with args passed through
- $DEKEN_HOME/virtualenv/bin/hy $DEKEN_HOME/deken.hy $@
+ tryrun_deken "$@"
+ ;;
+esac
diff --git a/developer/deken.hy b/developer/deken.hy
index b611a8d..9f22f2f 100644
--- a/developer/deken.hy
+++ b/developer/deken.hy
@@ -1,5 +1,39 @@
#!/usr/bin/env hy
-; deken upload --version 0.1 ./freeverb~/
+;; deken upload --version 0.1 ./freeverb~/
+
+;; This software is copyrighted by Chris McCormick, IOhannes m zmölnig and
+;; others.
+;; The following terms (the "Standard Improved BSD License") apply to all
+;; files associated with the software unless explicitly disclaimed in
+;; individual files:
+;;
+;; 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. The name of the author may not be used to endorse or promote
+;; products derived from this software without specific prior
+;; written permission.
+;;
+;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR
+;; 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.
+
(import sys)
(import os)
@@ -14,25 +48,23 @@
(import struct)
(import copy)
(try (import [ConfigParser [SafeConfigParser]])
- (catch [e ImportError] (import [configparser [SafeConfigParser]])))
+ (except [e ImportError] (import [configparser [SafeConfigParser]])))
(try (import [StringIO [StringIO]])
- (catch [e ImportError] (import [io [StringIO]])))
+ (except [e ImportError] (import [io [StringIO]])))
(import hashlib)
(import [getpass [getpass]])
(try (import [urlparse [urlparse]])
- (catch [e ImportError] (import [urllib.parse [urlparse]])))
-(import requests)
-(import easywebdav)
+ (except [e ImportError] (import [urllib.parse [urlparse]])))
(require hy.contrib.loop)
(def deken-home (os.path.expanduser (os.path.join "~" ".deken")))
(def config-file-path (os.path.abspath (os.path.join deken-home "config")))
-(def version (try (.rstrip (.read (open (os.path.join deken-home "VERSION"))) "\r\n") (catch [e Exception] (.get os.environ "DEKEN_VERSION" "0.1"))))
+(def version (try (.rstrip (.read (open (os.path.join deken-home "VERSION"))) "\r\n") (except [e Exception] (.get os.environ "DEKEN_VERSION" "0.1"))))
(def externals-host "puredata.info")
(def elf-arch-types {
- "EM_NONE" nil
+ "EM_NONE" None
"EM_386" "i386"
"EM_68K" "m68k"
"EM_IA_64" "x86_64"
@@ -50,135 +82,163 @@
"EM_BLAFKIN" "Analog Devices Blackfin"
"RESERVED" "RESERVED"})
-(def arm-cpu-arch ["Pre-v4" "v4" "v4T" "v5T" "v5TE" "v5TEJ" "v6" "v6KZ" "v6T2" "v6K" "v7"])
+;; values updated via https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=include/elf/arm.h;hb=HEAD#l93
+(def arm-cpu-arch
+ [
+ "Pre-v4"
+ "v4"
+ "v4T"
+ "v5T"
+ "v5TE"
+ "v5TEJ"
+ "v6"
+ "v6KZ"
+ "v6T2"
+ "v6K"
+ "v7"
+ "v6_M"
+ "v6S_M"
+ "v7E_M"
+ "v8"
+ "v8R"
+ "v8M_BASE"
+ "v8M_MAIN"
+ ])
(def win-types {
"0x014c" ["i386" 32]
"0x0200" ["x86_64" 64]
"0x8664" ["amd64" 64]})
-; algorithm to use to hash files
+;; algorithm to use to hash files
(def hasher hashlib.sha256)
(def hash-extension (.pop (hasher.__name__.split "_")))
-; get the externals' homedir install location for this platform - from s_path.c
-(def externals-folder
- (let [[system-name (platform.system)]]
- (cond
- [(in system-name ["Linux" "FreeBSD"]) (os.path.expandvars (os.path.join "$HOME" "pd-externals"))]
- [(= system-name "Darwin") (os.path.expandvars (os.path.join "$HOME" "Library" "Pd"))]
- [(= system-name "Windows") (os.path.expandvars (os.path.join "%AppData%" "Pd"))])))
+;; nil? has been removed from hy-0.12
+(try (nil? None) (except [e NameError] (defn nil? [x] (= x None))))
+
+;; in hy-0.12 'slice' has been replaced with 'cut'
+;; but we cannot replace 'cut' in hy>=0.12, because it is a built-in...
+(defn cut-slice [x y z] (cut x y z))
+(try (cut []) (except [e NameError] (defn cut-slice [x y z] (slice x y z))))
-; convert a string into bool, based on the string value
+;; convert a string into bool, based on the string value
(defn str-to-bool [s] (and (not (nil? s)) (not (in (.lower s) ["false" "f" "no" "n" "0" "nil" "none"]))))
;; join non-empty elements
(defn join-nonempty [joiner elements] (.join joiner (list-comp (str x) [x elements] x)))
-; concatenate two dictionaries - hylang's assoc is broken
+;; concatenate two dictionaries - hylang's assoc is broken
(defn dict-merge [d1 d2] (apply dict [d1] (or d2 {})))
-; apply attributes to objects in a functional way
-(defn set-attr [obj attr value] (do (setattr obj attr value) obj))
+;; apply attributes to objects in a functional way
+(defn set-attr [obj attr value] (setattr obj attr value) obj)
+;; get multiple attributes as list
+(defn get-attrs [obj attributes &optional default] (list-comp (getattr obj _default) [_ attributes]))
-; replace multiple words (given as pairs in <repls>) in a string <s>
+;; replace multiple words (given as pairs in <repls>) in a string <s>
(defn replace-words [s repls] (reduce (fn [a kv] (apply a.replace kv)) repls s))
-;; get a value at an index or a default
-(defn try-get [elements index &optional default] (try (get elements index) (catch [e IndexError] default)))
+;; get multiple values from a dict (give keys as list, get values as list)
+(defn get-values [coll keys] (list-comp (get coll _) [_ keys]))
+
+;; get a value at an index/key or a default
+(defn try-get [elements index &optional default]
+ (try (get elements index)
+ (except [e TypeError] default)
+ (except [e KeyError] default)
+ (except [e IndexError] default)))
-; read in the config file if present
-(def config
- (let [
- [config-file (SafeConfigParser)]
- [file-buffer (StringIO (+ "[default]\n" (try (.read (open config-file-path "r")) (catch [e Exception] ""))))]]
- (config-file.readfp file-buffer)
- (dict (config-file.items "default"))))
+;; read in the config file if present
+(defn read-config [configstring &optional [config-file (SafeConfigParser)]]
+ (config-file.readfp (StringIO configstring))
+ (dict (config-file.items "default")))
-; takes the externals architectures and turns them into a string
+(def config (read-config (+ "[default]\n" (try (.read (open config-file-path "r"))(except [e Exception] "")))))
+
+;; takes the externals architectures and turns them into a string
(defn get-architecture-strings [folder]
- (let [[archs (get-externals-architectures folder)]
- [sep-1 ")("]
- [sep-2 "-"]]
- (if archs
- (+ "(" (sep-1.join (set (list-comp (sep-2.join (list-comp (str a) [a arch])) [arch archs]))) ")")
- "")))
-
-; check if a particular file has an extension in a set
+ (defn _get_archs [archs sep-1 sep-2]
+ (if archs (+ "(" (sep-1.join (set (list-comp (sep-2.join (list-comp (str a) [a arch])) [arch archs]))) ")") ""))
+ (_get_archs (get-externals-architectures folder) ")(" "-"))
+
+;; check if a particular file has an extension in a set
(defn test-extensions [filename extensions]
(len (list-comp e [e extensions] (filename.endswith e))))
-; examine a folder for externals and return the architectures of those found
+;; examine a folder for externals and return the architectures of those found
(defn get-externals-architectures [folder]
- (sum (list-comp (cond
+ (sum (sorted (list-comp (cond
[(test-extensions f [".pd_linux" ".l_ia64" ".l_i386" ".l_arm" ".so"]) (get-elf-arch (os.path.join folder f) "Linux")]
[(test-extensions f [".pd_freebsd" ".b_i386"]) (get-elf-arch (os.path.join folder f) "FreeBSD")]
[(test-extensions f [".pd_darwin" ".d_fat" ".d_ppc"]) (get-mach-arch (os.path.join folder f))]
[(test-extensions f [".m_i386" ".dll"]) (get-windows-arch (os.path.join folder f))]
[(test-extensions f [".c" ".cpp" ".C" ".cxx" ".cc"]) [["Sources"]]]
- [true []])
- [f (os.listdir folder)]) []))
-
-; get architecture strings from a windows DLL
-; http://stackoverflow.com/questions/495244/how-can-i-test-a-windows-dll-to-determine-if-it-is-32bit-or-64bit
-(defn get-windows-arch [filename] (try (do-get-windows-arch filename) (catch [e Exception] [])))
-(defn do-get-windows-arch [filename]
- (let [[f (open filename "rb")]
- [[magic blah offset] (struct.unpack (str "<2s58sL") (f.read 64))]]
- ;(print magic offset)
- (if (= magic "MZ")
- ; has correct magic bytes
- (do
- (f.seek offset)
- (let [[[sig skip machine] (struct.unpack (str "<2s2sH") (f.read 6))]]
- ;(print sig (% "0x%04x" machine))
- (if (= sig "PE")
- ; has correct signature
- [(+ ["Windows"] (win-types.get (% "0x%04x" machine) ["unknown" "unknown"]))]
- (raise (Exception "Not a PE Executable.")))))
- (raise (Exception "Not a valid Windows dll.")))))
-
-; get architecture from an ELF (e.g. Linux)
+ [True []])
+ [f (os.listdir folder)]
+ (os.path.exists (os.path.join folder f)))) []))
+
+;; get architecture strings from a windows DLL
+;; http://stackoverflow.com/questions/495244/how-can-i-test-a-windows-dll-to-determine-if-it-is-32bit-or-64bit
+(defn get-windows-arch [filename] (try (do-get-windows-arch (open filename "rb")) (except [e Exception] [])))
+(defn do-get-windows-arch [f]
+ (setv [magic _ offset] (struct.unpack (str "<2s58sL") (f.read 64)))
+ (if (= magic "MZ") ; has correct magic bytes
+ (do
+ (f.seek offset)
+ (setv [sig _ machine] (struct.unpack (str "<2s2sH") (f.read 6)))
+ (if (= sig "PE") ; has correct signature
+ [(+ ["Windows"] (win-types.get (% "0x%04x" machine) ["unknown" "unknown"]))]
+ (raise (Exception "Not a PE Executable."))))
+ (raise (Exception "Not a valid Windows dll."))))
+
+;; get architecture from an ELF (e.g. Linux)
(defn get-elf-arch [filename oshint]
(import [elftools.elf.elffile [ELFFile]])
(import [elftools.common [exceptions]])
(try
- (let [[elf (ELFFile (open filename :mode "rb"))]]
- ;; TODO: check section .ARM.attributes for v number
- ;; python ./virtualenv/bin/readelf.py -p .ARM.attributes ...
- [[oshint (+ (elf-arch-types.get (elf.header.get "e_machine") nil) (or (parse-arm-elf-arch elf) "")) (int (slice (.get (elf.header.get "e_ident") "EI_CLASS") -2))]])
- (catch [e exceptions.ELFError] [])))
-
-; get architecture from a Darwin Mach-O file (OSX)
+ (do
+ (setv elf (ELFFile (open filename :mode "rb")))
+ ;; TODO: check section .ARM.attributes for v number
+ ;; python ./virtualenv/bin/readelf.py -p .ARM.attributes ...
+ [[oshint
+ (+ (elf-arch-types.get (elf.header.get "e_machine") None)
+ (or (parse-arm-elf-arch elf) ""))
+ (int (cut-slice (.get (elf.header.get "e_ident") "EI_CLASS") -2 None))]])
+ (except [e exceptions.ELFError] [])))
+
+;; get architecture from a Darwin Mach-O file (OSX)
(defn get-mach-arch [filename]
(import [macholib.MachO [MachO]])
(import [macholib.mach_o [MH_MAGIC_64 CPU_TYPE_NAMES]])
(try
- (let [[macho (MachO filename)]]
- (list-comp ["Darwin" (CPU_TYPE_NAMES.get h.header.cputype h.header.cputype) (if (= h.MH_MAGIC MH_MAGIC_64) 64 32)] [h macho.headers]))
- (catch [e ValueError] [])))
+ (list-comp ["Darwin" (CPU_TYPE_NAMES.get h.header.cputype h.header.cputype) (if (= h.MH_MAGIC MH_MAGIC_64) 64 32)] [h (. (MachO filename) headers)])
+ (except [e ValueError] [])))
+
-; gets the specific flavour of arm by hacking the .ARM.attributes ELF section
+
+;; gets the specific flavour of arm by hacking the .ARM.attributes ELF section
(defn parse-arm-elf-arch [arm-elf]
- (let [[arm-section (if arm-elf (try (arm-elf.get_section_by_name ".ARM.attributes")))]
- [data (and arm-section (.startswith (arm-section.data) "A") (.index (arm-section.data) "aeabi") (.pop (.split (arm-section.data) "aeabi")))]]
- (if data (do
- ; (print (struct.unpack (str "<s") (slice data 7)))
- (let [[[name bins] (.split (slice data 7) "\x00")]
- [arch (get arm-cpu-arch (ord (slice bins 1 2)))]]
- arch)))))
-
-; try to obtain a value from environment, then config file, then prompt user
+ (setv arm-section (if arm-elf (try (arm-elf.get_section_by_name ".ARM.attributes"))))
+ ;; we only support format 'A'
+ (setv A (try (bytes "A") (except [e TypeError] (bytes "A" "ascii"))))
+ ;; the arm cpu can be found in the 'aeabi' section
+ (setv data (and arm-section (.startswith (arm-section.data) A) (.index (arm-section.data) "aeabi") (.pop (.split (arm-section.data) "aeabi"))))
+ (if data
+ (get arm-cpu-arch (ord (get (get (.split (cut-slice data 7 None) "\x00" 1) 1) 1)))))
+
+;; try to obtain a value from environment, then config file, then prompt user
(defn get-config-value [name &rest default]
- (first (filter (fn [x] (not (nil? x))) [
- ; try to get the value from an environment variable
- (os.environ.get (+ "DEKEN_" (name.upper)))
- ; try to get the value from the config file
- (config.get name)
- ; finally, try the default
- (first default)])))
-
-; prompt for a particular config value for externals host upload
+ (first (filter (fn [x] (not (nil? x)))
+ [
+ ;; try to get the value from an environment variable
+ (os.environ.get (+ "DEKEN_" (name.upper)))
+ ;; try to get the value from the config file
+ (config.get name)
+ ;; finally, try the default
+ (first default)])))
+
+;; prompt for a particular config value for externals host upload
(defn prompt-for-value [name]
(raw_input (% (+
"Environment variable DEKEN_%s is not set and the config file %s does not contain a '%s = ...' entry.\n"
@@ -186,178 +246,202 @@
"Please enter %s for http://%s/: ")
(tuple [(name.upper) config-file-path name name externals-host]))))
-; calculate the sha256 hash of a file
-(defn hash-sum-file [filename]
- (let [[hashfn (hasher)]
- [blocksize 65536]
- [f (open filename :mode "rb")]
- [read-chunk (fn [] (f.read blocksize))]]
- (loop [[buf (read-chunk)]]
- (if (len buf) (do
- (hashfn.update buf)
- (recur (read-chunk)))))
- (let [[digest (hashfn.hexdigest)]
- [hashfilename (% "%s.%s" (tuple [filename hash-extension]))]]
- (.write (open hashfilename "w") digest)
- hashfilename)))
+;; calculate the sha256 hash of a file
+(defn hash-file [filename &optional [blocksize 65535] [hashfn (hasher)]]
+ (setv f (open filename :mode "rb"))
+ (setv read-chunk (fn [] (.read f blocksize)))
+ (while True
+ (setv buf (read-chunk))
+ (if-not buf (break))
+ (hashfn.update buf))
+ (hashfn.hexdigest))
+
+(defn hash-sum-file [filename &optional [blocksize 65535]]
+ (setv hashfilename (% "%s.%s" (tuple [filename hash-extension])))
+ (.write (open hashfilename :mode "w") (hash-file filename blocksize))
+ hashfilename)
;; handling GPG signatures
(try (import gnupg)
- ;; read a value from the gpg config
+ ;; read a value from the gpg config
(except [e ImportError] (defn gpg-sign-file [filename] (print (% "Unable to GPG sign '%s'\n" filename) "'gnupg' module not loaded")))
(else
+ (defn gpg-unavail-error [state &optional ex]
+ (print (% "WARNING: GPG %s failed:" state))
+ (if ex (print ex))
+ (print "Do you have 'gpg' installed?")
+ (print "- If you've received numerous errors during the initial installation,")
+ (print " you probably should install 'python-dev', 'libffi-dev' and 'libssl-dev'")
+ (print " and re-run `deken install`")
+ (print "- On OSX you might want to install the 'GPG Suite'"))
(defn gpg-get-config [gpg id]
- (let [[configdir (cond [gpg.gnupghome gpg.gnupghome] [True (os.path.join "~" ".gnupg")])]
- [configfile (os.path.expanduser (os.path.join configdir "gpg.conf"))]]
(try
- (get (list-comp (get (.split (.strip x)) 1) [x (.readlines ( open configfile))] (.startswith (.lstrip x) (.strip id) )) -1)
- (catch [e [IOError IndexError]] None))))
+ (get
+ (list-comp
+ (get (.split (.strip x)) 1)
+ [x
+ (.readlines
+ ( open
+ (os.path.expanduser
+ (os.path.join
+ (or gpg.gnupghome (os.path.join "~" ".gnupg"))
+ "gpg.conf"))
+ ))]
+ (.startswith (.lstrip x) (.strip id) )) -1)
+ (except [e [IOError IndexError]] None)))
;; get the GPG key for signing
(defn gpg-get-key [gpg]
- (let [[keyid (get-config-value "key_id" (gpg-get-config gpg "default-key"))]]
- (try
- (car (list-comp k [k (gpg.list_keys true)] (cond [keyid (.endswith (.upper (get k "keyid" )) (.upper keyid) )] [True True])))
- (catch [e IndexError] None))))
+ (setv keyid (get-config-value "key_id" (gpg-get-config gpg "default-key")))
+ (try
+ (car (list-comp k
+ [k (gpg.list_keys True)]
+ (cond [keyid (.endswith (.upper (get k "keyid" )) (.upper keyid) )]
+ [True True])))
+ (except [e IndexError] None)))
;; generate a GPG signature for a particular file
- (defn do-gpg-sign-file [filename signfile]
+ (defn do-gpg-sign-file [filename signfile gnupghome use-agent]
(print (% "Attempting to GPG sign '%s'" filename))
- (let [[gnupghome (get-config-value "gpg_home")]
- [use-agent (str-to-bool (get-config-value "gpg_agent"))]
- [gpg (set-attr (apply gnupg.GPG []
- (dict-merge (dict-merge {} (if gnupghome {"gnupghome" gnupghome}))
- (if use-agent {"use_agent" true})))
- "decode_errors" "replace")]
- [sec-key (gpg-get-key gpg)]
- [keyid (try (get sec-key "keyid") (catch [e KeyError] None) (catch [e TypeError] None))]
- [uid (try (get (get sec-key "uids") 0) (catch [e KeyError] None) (catch [e TypeError] None))]
- [passphrase (if (and (not use-agent) keyid)
- (do
- (print (% "You need a passphrase to unlock the secret key for\nuser: %s ID: %s\nin order to sign %s" (tuple [uid keyid filename])))
- (getpass "Enter GPG passphrase: " )))]
- [signconfig (dict-merge (dict-merge {"detach" true}
- (if keyid {"keyid" keyid}))
- (if passphrase {"passphrase" passphrase}))]]
- (if (and (not use-agent) passphrase)
- (print "No passphrase and not using gpg-agent...trying to sign anyhow"))
- (try
- (let [[sig (if gpg (apply gpg.sign_file [(open filename "rb")] signconfig))]
- [signfile (+ filename ".asc")]]
- (if (hasattr sig "stderr")
- (print (try (str sig.stderr) (catch [e UnicodeEncodeError] (.encode sig.stderr "utf-8")))))
- (if (not sig)
- (do
- (print "WARNING: Could not GPG sign the package.")
- None)
- (do
- (.write (open signfile "w") (str sig))
- signfile)))
- (catch [e OSError] (print (.join "\n"
- ["WARNING: GPG signing failed:"
- str(e)
- "Do you have 'gpg' (on OSX: 'GPG Suite') installed?"]))))))
+ (setv gpg
+ (try
+ (set-attr
+ (apply gnupg.GPG []
+ (dict-merge
+ (dict-merge {} (if gnupghome {"gnupghome" gnupghome}))
+ (if use-agent {"use_agent" True})))
+ "decode_errors" "replace")
+ (except [e OSError] (gpg-unavail-error "init" e))))
+ (if gpg (do
+ (setv [keyid uid] (list-comp (try-get (gpg-get-key gpg) _ None) [_ ["keyid" "uids"]]))
+ (setv uid (try-get uid 0 None))
+ (setv passphrase
+ (if (and (not use-agent) keyid)
+ (do
+ (print (% "You need a passphrase to unlock the secret key for\nuser: %s ID: %s\nin order to sign %s"
+ (tuple [uid keyid filename])))
+ (getpass "Enter GPG passphrase: " ))))
+ (setv signconfig (dict-merge (dict-merge {"detach" True}
+ (if keyid {"keyid" keyid}))
+ (if passphrase {"passphrase" passphrase})))
+
+ (if (and (not use-agent) (not passphrase))
+ (print "No passphrase and not using gpg-agent...trying to sign anyhow"))
+ (try
+ (do
+ (setv sig (if gpg (apply gpg.sign_file [(open filename "rb")] signconfig)))
+; (if (hasattr sig "stderr")
+; (print (try (str sig.stderr) (except [e UnicodeEncodeError] (.encode sig.stderr "utf-8")))))
+ (if (not sig)
+ (print "WARNING: Could not GPG sign the package.")
+ (do
+ (with [f (open signfile "w")] (f.write (str sig)))
+ signfile)))
+ (except [e OSError] (gpg-unavail-error "signing" e))))))
;; sign a file if it is not already signed
(defn gpg-sign-file [filename]
- (let [[signfile (+ filename ".asc")]]
- (if (os.path.exists signfile)
- (do
- (print (% "NOTICE: not GPG-signing already signed file '%s'\nNOTICE: delete '%s' to re-sign" (, filename signfile)))
- signfile)
- (do-gpg-sign-file filename signfile))))))
-
-; execute a command inside a directory
+ (setv signfile (+ filename ".asc"))
+ (setv gpghome (get-config-value "gpg_home"))
+ (setv gpgagent (str-to-bool (get-config-value "gpg_agent")))
+ (if (os.path.exists signfile)
+ (do
+ (print (% "NOTICE: not GPG-signing already signed file '%s'\nNOTICE: delete '%s' to re-sign" (, filename signfile)))
+ signfile)
+ (do-gpg-sign-file filename signfile gpghome gpgagent)))))
+
+;; execute a command inside a directory
(defn in-dir [destination f &rest args]
- (let [
- [last-dir (os.getcwd)]
- [new-dir (os.chdir destination)]
- [result (apply f args)]]
- (os.chdir last-dir)
- result))
-
-; zip up a single directory
-; http://stackoverflow.com/questions/1855095/how-to-create-a-zip-archive-of-a-directory
+ (setv last-dir (os.getcwd))
+ (os.chdir destination)
+ (setv result (apply f args))
+ (os.chdir last-dir)
+ result)
+
+;; zip up a single directory
+;; http://stackoverflow.com/questions/1855095/how-to-create-a-zip-archive-of-a-directory
+(defn zip-file [filename]
+ (try (zipfile.ZipFile filename "w" :compression zipfile.ZIP_DEFLATED)
+ (except [e RuntimeError] (zipfile.ZipFile filename "w"))))
(defn zip-dir [directory-to-zip archive-file]
- (let [[zip-file (+ archive-file ".zip")]
- [zipf (try (zipfile.ZipFile zip-file "w" :compression zipfile.ZIP_DEFLATED)
- (catch [e RuntimeError] (zipfile.ZipFile zip-file "w")))]
- [root-basename (os.path.basename directory-to-zip)]
- [root-path (os.path.join directory-to-zip "..")]]
- (for [[root dirs files] (os.walk directory-to-zip)]
- (for [file files]
- (let [[file-path (os.path.join root file)]]
- (zipf.write file-path (os.path.relpath file-path root-path)))))
- (zipf.close)
- zip-file))
-
-; tar up the directory
+ (setv zip-filename (+ archive-file ".zip"))
+ (with [f (zip-file zip-filename)]
+ (for [[root dirs files] (os.walk directory-to-zip)]
+ (for [file-path (list-comp (os.path.join root file) [file files])]
+ (if (os.path.exists file-path)
+ (f.write file-path (os.path.relpath file-path (os.path.join directory-to-zip "..")))))))
+ zip-filename)
+
+;; tar up the directory
(defn tar-dir [directory-to-tar archive-file]
- (let [[tar-file (+ archive-file ".tar.gz")]
- [tarf (tarfile.open tar-file "w:gz")]]
- (do
- (.add tarf directory-to-tar)
- (.close tarf)
- tar-file)))
-
-; do we use zip or tar on this archive?
+ (setv tar-file (+ archive-file ".tar.gz"))
+ (defn tarfilter [tarinfo]
+ (setv tarinfo.name (os.path.relpath tarinfo.name (os.path.join directory-to-tar "..")))
+ tarinfo)
+ (with [f (tarfile.open tar-file "w:gz")]
+ (f.add directory-to-tar :filter tarfilter))
+ tar-file)
+
+;; do we use zip or tar on this archive?
(defn archive-extension [rootname]
(if (or (in "(Windows" rootname) (not (in "(" rootname))) ".zip" ".tar.gz"))
-; automatically pick the correct archiver - windows or "no arch" = zip
+;; automatically pick the correct archiver - windows or "no arch" = zip
(defn archive-dir [directory-to-archive rootname]
- (let [[ext (archive-extension rootname)]]
- ((if (= ext ".zip") zip-dir tar-dir) directory-to-archive rootname)))
+ ((if (= (archive-extension rootname) ".zip") zip-dir tar-dir) directory-to-archive rootname))
-; naive check, whether we have an archive: compare against known suffixes
+;; naive check, whether we have an archive: compare against known suffixes
(defn is-archive? [filename]
(len (list-comp f [f [".zip" ".tar.gz" ".tgz"]] (.endswith (filename.lower) f))))
-; upload a zipped up package to puredata.info
+;; upload a zipped up package to puredata.info
(defn upload-file [filepath destination username password]
+ ;; get username and password from the environment, config, or user input
+ (import easywebdav)
(if filepath
- (let [
- ;; get username and password from the environment, config, or user input
- [filename (os.path.basename filepath)]
- [[pkg ver arch ext] (parse-filename filename)]
- [url (urlparse destination)]
- [proto (or url.scheme "https")]
- [host (or url.netloc externals-host)]
- [path (str (replace-words (or (.rstrip url.path "/") "/Members/%u/software/%p/%v") (,
- (, "%u" username) (, "%p" pkg) (, "%v" (or ver "")))))]
- [remotepath (+ path "/" filename)]
- [url (+ proto "://" host path)]
- [dav (apply easywebdav.connect [host] {"username" username "password" password "protocol" proto})]]
- (print (+ "Uploading " filename " to " url))
- (try
- (do
- ; make sure all directories exist
- (dav.mkdirs path)
- ; upload the package file
- (dav.upload filepath remotepath))
- (catch [e easywebdav.client.OperationFailed]
- (sys.exit (+
- (% "Couldn't upload to %s!\n" url)
- (% "Are you sure you have the correct username and password set for '%s'?\n" host)
- (% "Please ensure the folder '%s' exists on the server and is writeable." path))))))))
+ (do
+ (setv filename (os.path.basename filepath))
+ (setv [pkg ver _ _] (parse-filename filename))
+ (setv url (urlparse destination))
+ (setv proto (or url.scheme "https"))
+ (setv host (or url.netloc externals-host))
+ (setv path
+ (str
+ (replace-words
+ (or (.rstrip url.path "/") "/Members/%u/software/%p/%v")
+ (, (, "%u" username) (, "%p" pkg) (, "%v" (or ver ""))))))
+ (setv url (+ proto "://" host path))
+ (setv dav (apply easywebdav.connect [host] {"username" username "password" password "protocol" proto}))
+ (print (+ "Uploading " filename " to " url))
+ (try
+ (do
+ ;; make sure all directories exist
+ (dav.mkdirs path)
+ ;; upload the package file
+ (dav.upload filepath (+ path "/" filename)))
+ (except [e easywebdav.client.OperationFailed]
+ (sys.exit (+
+ (% "Couldn't upload to %s!\n" url)
+ (% "Are you sure you have the correct username and password set for '%s'?\n" host)
+ (% "Please ensure the folder '%s' exists on the server and is writeable." path))))))))
+
;; upload a list of archives (given the archive-filename it will also upload some extra-files (sha256, gpg,...))
(defn upload-package [pkg destination username password]
- (do
- (print "Uploading package" pkg)
- (upload-file (hash-sum-file pkg) destination username password)
- (upload-file pkg destination username password)
- (upload-file (gpg-sign-file pkg) destination username password)))
+ (print "Uploading package" pkg)
+ (upload-file (hash-sum-file pkg) destination username password)
+ (upload-file pkg destination username password)
+ (upload-file (gpg-sign-file pkg) destination username password))
(defn upload-packages [pkgs destination username password skip-source]
- (do (if (not skip-source) (check-sources (set (list-comp (filename-to-namever pkg) [pkg pkgs]))
- (set (list-comp (has-sources? pkg) [pkg pkgs]))
- (if (= "puredata.info"
- (.lower (or (getattr (urlparse destination) "netloc") externals-host)))
- username)))
- (for [pkg pkgs] (upload-package pkg destination username password))))
-
-; compute the zipfile name for a particular external on this platform
+ (if (not skip-source) (check-sources (set (list-comp (filename-to-namever pkg) [pkg pkgs]))
+ (set (list-comp (has-sources? pkg) [pkg pkgs]))
+ (if (= "puredata.info"
+ (.lower (or (getattr (urlparse destination) "netloc") externals-host)))
+ username)))
+ (for [pkg pkgs] (upload-package pkg destination username password)))
+
+;; compute the zipfile name for a particular external on this platform
(defn make-archive-basename [folder version]
- (+ (.rstrip folder "/\\")
+ (+ (os.path.basename folder)
(cond [(nil? version) (sys.exit
(+ (% "No version for '%s'!\n" folder)
" Please provide the version-number via the '--version' flag.\n"
@@ -368,43 +452,43 @@
[True ""])
(get-architecture-strings folder) "-externals"))
-; create additional files besides archive: hash-file and gpg-signature
+;; create additional files besides archive: hash-file and gpg-signature
(defn archive-extra [zipfile]
- (do
(print "Packaging" zipfile)
(hash-sum-file zipfile)
(gpg-sign-file zipfile)
- zipfile))
+ zipfile)
-; parses a filename into a (pkgname version archs extension) tuple
-; missing values are nil
+;; parses a filename into a (pkgname version archs extension) tuple
+;; missing values are None
(defn parse-filename [filename]
(list-comp (get
- ; parse filename with a regex
+ ;; parse filename with a regex
(re.split r"(.*/)?(.+?)(-v(.+)-)?((\([^\)]+\))+|-)*-externals\.([a-z.]*)" filename) x)
- ; extract only the fields of interested
+ ;; extract only the fields of interested
[x [2 4 5 7]]))
(defn filename-to-namever [filename]
- (let [[[pkg ver arch ext] (parse-filename filename)]] (join-nonempty "/" [pkg ver])))
+ (join-nonempty "/" (get-values (parse-filename filename) [0 1])))
;; check if the list of archs contains sources (or is arch-independent)
(defn is-source-arch? [arch] (or (not arch) (in "(Sources)" arch)))
;; check if a package contains sources (and returns name-version to be used in a SET of packages with sources)
-(defn has-sources? [filename] (let [[[pkg ver arch ext] (parse-filename filename)]]
- (if (is-source-arch? arch) (filename-to-namever filename))))
+(defn has-sources? [filename]
+ (if (is-source-arch? (try-get (parse-filename filename) 2)) (filename-to-namever filename)))
;; check if the given package has a sources-arch on puredata.info
(defn check-sources at puredata-info [pkg username]
- (do (print (% "Checking puredata.info for Source package for '%s'" pkg))
- (in pkg
- ;; list of package/version matching 'pkg' that have 'Source' archictecture
- (list-comp
- (has-sources? p)
- [p
- (list-comp
- (try-get (.split (try-get (.split x "\t") 1) "/") -1) ;; filename part of the download URL
- [x (.splitlines (getattr (requests.get (% "http://deken.puredata.info/search?name=%s" (get (.split pkg "/") 0))) "text"))]
- (= username (try-get (.split x "\t") 2)))]))))
+ (import requests)
+ (print (% "Checking puredata.info for Source package for '%s'" pkg))
+ (in pkg
+ ;; list of package/version matching 'pkg' that have 'Source' archictecture
+ (list-comp
+ (has-sources? p)
+ [p
+ (list-comp
+ (try-get (.split (try-get (.split x "\t") 1) "/") -1) ; filename part of the download URL
+ [x (.splitlines (getattr (requests.get (% "http://deken.puredata.info/search?name=%s" (get (.split pkg "/") 0))) "text"))]
+ (= username (try-get (.split x "\t") 2)))])))
;; check if sources archs are present by comparing a SET of packagaes and a SET of packages-with-sources
(defn check-sources [pkgs sources &optional puredata-info-user]
@@ -429,69 +513,74 @@
(get-config-value "password")))
(getpass (% "Please enter password for uploading as '%s': " username))))
-; the executable portion of the different sub-commands that make up the deken tool
-(def commands {
- ; zip up a set of built externals
- :package (fn [args]
- ;; are they asking to package a directory?
- (list-comp
- (if (os.path.isdir name)
- ; if asking for a directory just package it up
- (archive-extra (archive-dir name (make-archive-basename (.rstrip name "/\\") args.version)))
- (sys.exit (% "Not a directory '%s'!" name)))
- (name args.source)))
- ; upload packaged external to pure-data.info
- :upload (fn [args]
- (let [[username (or (get-config-value "username") (prompt-for-value "username"))]
- [password (get-upload-password username args.ask-password)]]
- (do
- (upload-packages (list-comp (cond [(os.path.isfile x)
- (if (is-archive? x) x (sys.exit (% "'%s' is not an externals archive!" x)))]
- [(os.path.isdir x) (get ((:package commands) (set-attr (copy.deepcopy args) "source" [x])) 0)]
- [True (sys.exit (% "Unable to process '%s'!" x))])
- (x args.source))
- (or (getattr args "destination") (get-config-value "destination" ""))
- username password args.no-source-error))
- ;; if we reach this line, upload has succeeded; so let's try storing the (non-empty) password in the keyring
- (if password
- (try (do
- (import keyring)
- (keyring.set_password "deken" username password))))))
- ; self-update deken
- :upgrade (fn [args]
- (sys.exit "The upgrade script isn't here, it's in the Bash wrapper!"))})
-
-; kick things off by using argparse to check out the arguments supplied by the user
+;; the executable portion of the different sub-commands that make up the deken tool
+(def commands
+ {
+ ;; zip up a set of built externals
+ :package (fn [args]
+ ;; are they asking to package a directory?
+ (list-comp
+ (if (os.path.isdir name)
+ ;; if asking for a directory just package it up
+ (archive-extra (archive-dir name (make-archive-basename (os.path.normpath name) args.version)))
+ (sys.exit (% "Not a directory '%s'!" name)))
+ (name args.source)))
+ ;; upload packaged external to pure-data.info
+ :upload (fn [args]
+ (setv username (or (get-config-value "username") (prompt-for-value "username")))
+ (setv password (get-upload-password username args.ask-password))
+ (upload-packages (list-comp (cond [(os.path.isfile x)
+ (if (is-archive? x) x (sys.exit (% "'%s' is not an externals archive!" x)))]
+ [(os.path.isdir x) (get ((:package commands) (set-attr (copy.deepcopy args) "source" [x])) 0)]
+ [True (sys.exit (% "Unable to process '%s'!" x))])
+ (x args.source))
+ (or (getattr args "destination") (get-config-value "destination" ""))
+ username password args.no-source-error)
+ ;; if we reach this line, upload has succeeded; so let's try storing the (non-empty) password in the keyring
+ (if password
+ (try (do
+ (import keyring)
+ (keyring.set_password "deken" username password)))))
+ ;; the rest should have been caught by the wrapper script
+ :upgrade (fn [args] (sys.exit "'upgrade' not implemented for this platform!"))
+ :update (fn [args] (sys.exit "'upgrade' not implemented for this platform!"))
+ :install (fn [args] (sys.exit "'install' not implemented for this platform!"))})
+
+;; kick things off by using argparse to check out the arguments supplied by the user
(defn main []
- (let [
- [arg-parser (apply argparse.ArgumentParser [] {"prog" "deken" "description" "Deken is a build tool for Pure Data externals."})]
- [arg-subparsers (apply arg-parser.add_subparsers [] {"help" "-h for help." "dest" "command"})]
- [arg-package (apply arg-subparsers.add_parser ["package"])]
- [arg-upload (apply arg-subparsers.add_parser ["upload"])]
- [arg-upgrade (apply arg-subparsers.add_parser ["upgrade"])]]
- (apply arg-parser.add_argument ["--version"] {"action" "version" "version" version "help" "Outputs the version number of Deken."})
- (apply arg-package.add_argument ["source"] {"nargs" "+"
- "metavar" "SOURCE"
- "help" "The path to a directory of externals, abstractions, or GUI plugins to be packaged."})
- (apply arg-package.add_argument ["--version" "-v"] {"help" "An external version number to insert into the package name."
- "default" nil
- "required" false})
- (apply arg-upload.add_argument ["source"] {"nargs" "+"
- "metavar" "PACKAGE"
- "help" "The path to an externals/abstractions/plugins zipfile to be uploaded, or a directory which will be packaged first automatically."})
- (apply arg-upload.add_argument ["--version" "-v"] {"help" "An external version number to insert into the package name. (in case a package is created)"
- "default" nil
- "required" false})
- (apply arg-upload.add_argument ["--destination" "-d"] {"help" "The destination folder to upload the file into (defaults to /Members/USER/software/PKGNAME/VERSION/)." "default" "" "required" false})
- (apply arg-upload.add_argument ["--ask-password" "-P"] {"action" "store_true" "help" "Ask for upload password (rather than using password-manager." "default" "" "required" false})
- (apply arg-upload.add_argument ["--no-source-error"] {"action" "store_true" "help" "Force-allow uploading of packages without sources." "required" false})
- (let [
- [arguments (.parse_args arg-parser)]
- [command (.get commands (keyword arguments.command))]]
- (print "Deken" version)
- (if command (command arguments) (.print_help arg-parser)))))
+ (print "Deken" version)
+
+ (setv arg-parser
+ (apply argparse.ArgumentParser [] {"prog" "deken" "description" "Deken is a build tool for Pure Data externals."}))
+ (setv arg-subparsers (apply arg-parser.add_subparsers [] {"help" "-h for help." "dest" "command" "metavar" "{package,upload}"}))
+ (setv arg-package (apply arg-subparsers.add_parser ["package"]))
+ (setv arg-upload (apply arg-subparsers.add_parser ["upload"]))
+ (apply arg-subparsers.add_parser ["install"])
+ (apply arg-subparsers.add_parser ["upgrade"])
+ (apply arg-subparsers.add_parser ["update"])
+ (apply arg-parser.add_argument ["--version"] {"action" "version" "version" version "help" "Outputs the version number of Deken."})
+ (apply arg-package.add_argument ["source"]
+ {"nargs" "+"
+ "metavar" "SOURCE"
+ "help" "The path to a directory of externals, abstractions, or GUI plugins to be packaged."})
+ (apply arg-package.add_argument ["--version" "-v"] {"help" "An external version number to insert into the package name."
+ "default" None
+ "required" False})
+ (apply arg-upload.add_argument ["source"] {"nargs" "+"
+ "metavar" "PACKAGE"
+ "help" "The path to an externals/abstractions/plugins zipfile to be uploaded, or a directory which will be packaged first automatically."})
+ (apply arg-upload.add_argument ["--version" "-v"] {"help" "An external version number to insert into the package name. (in case a package is created)"
+ "default" None
+ "required" False})
+ (apply arg-upload.add_argument ["--destination" "-d"] {"help" "The destination folder to upload the file into (defaults to /Members/USER/software/PKGNAME/VERSION/)." "default" "" "required" False})
+ (apply arg-upload.add_argument ["--ask-password" "-P"] {"action" "store_true" "help" "Ask for upload password (rather than using password-manager." "default" "" "required" False})
+ (apply arg-upload.add_argument ["--no-source-error"] {"action" "store_true" "help" "Force-allow uploading of packages without sources." "required" False})
+
+ (setv arguments (.parse_args arg-parser))
+ (setv command (.get commands (keyword arguments.command)))
+ (if command (command arguments) (.print_help arg-parser)))
(if (= __name__ "__main__")
(try
(main)
- (catch [e KeyboardInterrupt] (print "\n[interrupted by user]"))))
+ (except [e KeyboardInterrupt] (print "\n[interrupted by user]"))))
diff --git a/developer/deken.py b/developer/deken.py
new file mode 100644
index 0000000..80f5f7d
--- /dev/null
+++ b/developer/deken.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This software is copyrighted by Chris McCormick and others. The following
+# terms (the "Standard Improved BSD License") apply to all files associated
+# with the software unless explicitly disclaimed in individual files:
+#
+# 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. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR
+# 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.
+
+# this is a thin wrapper around deken.hy, to launch it without a
+# HY executable (or file associations)
+
+import os
+import hy
+hy.importer.import_file_to_module(
+ "__main__",
+ os.path.join(os.path.dirname(os.path.realpath(__file__)), "deken.hy"))
diff --git a/developer/requirements.txt b/developer/requirements.txt
index b85b6d0..f30691b 100644
--- a/developer/requirements.txt
+++ b/developer/requirements.txt
@@ -1,9 +1,9 @@
-hy==0.11.0
+hy==0.12.1
easywebdav==1.2.0
-pyelftools==0.23
-macholib==1.7
-python-gnupg==0.3.7
-keyring==4.0
-ndg_httpsclient==0.4.0
+pyelftools==0.24
+macholib==1.8
+python-gnupg==0.4.1
+keyring==10.4.0
+ndg_httpsclient==0.4.3
pyasn1==0.1.9
-pyOpenSSL==0.15.1
+pyOpenSSL==16.2.0
diff --git a/developer/server/README.md b/developer/server/README.md
deleted file mode 100644
index 7f5bb7f..0000000
--- a/developer/server/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-server-side search-scripts
-===
-
-stubs for server-side scripts used search for packages and present them in a
-form, that is easily understood by the deken-plugin.
-
-### puredata.info
-searches for files (IAEMfile) and software-center releases (PSCFile) that match
-a given pattern.
-
diff --git a/developer/server/puredata_info.py b/developer/server/puredata_info.py
deleted file mode 100644
index 8cd026a..0000000
--- a/developer/server/puredata_info.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Import a standard function, and get the HTML request and response objects.
-from Products.PythonScripts.standard import html_quote
-from Products.PythonScripts.standard import url_unquote
-request = container.REQUEST
-RESPONSE = request.RESPONSE
-
-qs = request['QUERY_STRING']
-
-args = {}
-if qs:
- for arg in qs.split('&'):
- try:
- key, val = arg.split('=', 1)
- except ValueError:
- key = arg
- val = ''
- if key in args:
- args[key].append(val)
- else:
- args[key] = [val]
-
-
-def getNameVersion(filename):
- filename = filename.split('-externals', 1)[0] .split('(')[0]
- pkgver = filename.split('-v')
- if len(pkgver) > 1:
- pkg = '-v'.join(pkgver[:-1])
- ver = pkgver[-1].strip('-').strip()
- else:
- pkg = pkgver[0]
- ver = ""
- return (pkg.strip('-').strip(), ver)
-
-
-def showPackage(obj, url, filename):
- (name, version) = getNameVersion(filename)
- title = obj.Title().replace('\t', ' ')
- if (name not in title) or (version not in title):
- if version:
- title = "%s/%s (%s)" % (name, version, title)
- else:
- title = "%s (%s)" % (name, title)
- date = obj.Date().replace('\t', ' ')
- owner = obj.owner_info()['id'].replace('\t', ' ')
- print("%s\t%s\t%s\t%s" % (title, url, owner, date))
- return printed
-
-mytypes = ('IAEMFile', 'PSCFile')
-suffixes = ['zip', 'tgz', 'tar.gz']
-
-for t in mytypes:
- results = context.portal_catalog(portal_type=t)
- for i in results:
- url = url_unquote(i.getURL())
- FileName = url.split('/')[-1]
- filename=FileName.lower()
- match = False
- for suffix in suffixes:
- if filename.endswith("-externals.%s" % (suffix,)):
- match = True
- break
- if not match:
- continue
- if 'name' in args:
- (fname, version) = getNameVersion(filename)
- match = False
- for name in args['name']:
- name=name.lower()
- if name in fname:
- match = True
- break
- if not match:
- continue
- print("%s" % showPackage(i.getObject(), url, FileName))
-
-# make sure there is *some* content in the page
-if not printed:
- print("")
-
-return printed
diff --git a/developer/windows/deken-windows.py b/developer/windows/deken-windows.py
deleted file mode 100644
index 0f71df1..0000000
--- a/developer/windows/deken-windows.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import os
-import hy
-hy.importer.import_file_to_module("__main__", os.path.join(os.path.dirname(os.path.realpath(__file__)), "deken.hy"))
-#import deken
-# deken.main()
diff --git a/developer/windows/deken.bat b/developer/windows/deken.bat
index 7ae7c7d..41ebfb0 100644
--- a/developer/windows/deken.bat
+++ b/developer/windows/deken.bat
@@ -1,3 +1,3 @@
@set REF=%~dp0\windows\portable-python\App
@set PYTHONPATH=%REF%\Lib;%REF%\DLLs;%REF%\libs;%REF%\site-packages-alt
-@%REF%\python.exe %~dp0\deken-windows.py %*
+@%REF%\python.exe %~dp0\deken.py %*
diff --git a/plugin-config.md b/plugin-config.md
index e6f9032..ecb8489 100644
--- a/plugin-config.md
+++ b/plugin-config.md
@@ -1,4 +1,4 @@
-the deken-plugin for Pd can be configured via a simple text-file.
+The deken-plugin for Pd can be configured via a simple text-file.
# deken-plugin.conf
@@ -28,7 +28,7 @@ or into a system-specific place (`$PdPath` resp `%PdPath%` are the installation
Here are the possible values:
* `installpath` = Path where you would want to install the externals
- (default: the first writeable path in the standard search paths)
+ (default: the first writable path in the standard search paths)
(note: you should *always* use `/` as path-separator, even on W32!)
--
deken packaging
More information about the pkg-multimedia-commits
mailing list