source: trunk/base/portmgr/jobs/port_binary_distributable.tcl

Last change on this file was 139862, checked in by jmr@…, 3 years ago

set conflicts for cc-by and cc-by-sa

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 11.9 KB
Line 
1#!/bin/sh
2# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
3# \
4if type -fp port-tclsh >/dev/null; then exec port-tclsh "$0" "$@"; else exec /usr/bin/tclsh "$0" "$@"; fi
5# $Id: port_binary_distributable.tcl 139862 2015-08-30 11:45:34Z jmr@macports.org $
6#
7# Check that binaries of a port are distributable by looking at its license
8# and the licenses of its dependencies.
9#
10# Expected format: A {B C} means the license is A plus either B or C.
11#
12# Exit status:
13# 0: distributable
14# 1: non-distributable
15# 2: error
16
17
18set MY_VERSION 0.1
19
20array set portsSeen {}
21
22set check_deptypes {depends_build depends_lib}
23
24
25# Notes:
26# 'Restrictive/Distributable' means a non-free license that nonetheless allows
27# distributing binaries.
28# 'Restrictive' means a non-free license that does not allow distributing
29# binaries, and is thus not in the list.
30# 'Permissive' is a catchall for other licenses that allow
31# modification and distribution of source and binaries.
32# 'Copyleft' means a license that requires source code to be made available,
33# and derivative works to be licensed the same as the original.
34# 'GPLConflict' should be added if the license conflicts with the GPL (and its
35# variants like CeCILL and the AGPL) and is not in the list of licenses known
36# to do so below.
37# 'Noncommercial' means a license that prohibits commercial use.
38set good_licenses {afl agpl apache apsl artistic autoconf beopen bitstreamvera \
39                   boost bsd bsd-old cc-by cc-by-sa cddl cecill cecill-b cecill-c cnri copyleft \
40                   cpl curl epl fpll fontconfig freetype gd gfdl gpl \
41                   gplconflict ibmpl ijg isc jasper lgpl libtool lppl mit \
42                   mpl ncsa noncommercial openldap openssl permissive php \
43                   psf public-domain qpl restrictive/distributable ruby \
44                   sleepycat ssleay tcl/tk vim w3c wtfpl wxwidgets x11 zlib zpl}
45foreach lic $good_licenses {
46    set license_good($lic) 1
47}
48
49proc all_licenses_except { args } {
50    global good_licenses
51    set remaining $good_licenses
52    foreach arg $args {
53        set remaining [lsearch -inline -all -not -exact $remaining $arg]
54    }
55    return [list $remaining]
56}
57
58# keep these values sorted
59array set license_conflicts \
60    "afl {agpl cecill gpl}
61    agpl {afl apache-1 apache-1.1 apsl beopen bsd-old cc-by-1 cc-by-2 cc-by-2.5 cc-by-3 cc-by-sa cddl cecill cnri cpl epl gd gpl-1 gpl-2 gplconflict ibmpl lppl mpl noncommercial openssl php qpl restrictive/distributable ruby ssleay zpl-1}
62    agpl-1 {apache freetype gpl-3 gpl-3+ lgpl-3 lgpl-3+}
63    apache {agpl-1 cecill gpl-1 gpl-2}
64    apache-1 {agpl gpl}
65    apache-1.1 {agpl gpl}
66    apsl {agpl cecill gpl}
67    beopen {agpl cecill gpl}
68    bsd-old {agpl cecill gpl}
69    cc-by-1 {agpl cecill gpl}
70    cc-by-2 {agpl cecill gpl}
71    cc-by-2.5 {agpl cecill gpl}
72    cc-by-3 {agpl cecill gpl}
73    cc-by-sa {agpl cecill gpl}
74    cddl {agpl cecill gpl}
75    cecill {afl agpl apache apsl beopen bsd-old cc-by-1 cc-by-2 cc-by-2.5 cc-by-3 cc-by-sa cddl cnri cpl epl gd gplconflict ibmpl lppl mpl noncommercial openssl php qpl restrictive/distributable ruby ssleay zpl-1}
76    cnri {agpl cecill gpl}
77    cpl {agpl cecill gpl}
78    epl {agpl cecill gpl}
79    freetype {agpl-1 gpl-2}
80    gd {agpl cecill gpl}
81    gpl {afl apache-1 apache-1.1 apsl beopen bsd-old cc-by-1 cc-by-2 cc-by-2.5 cc-by-3 cc-by-sa cddl cnri cpl epl gd gplconflict ibmpl lppl mpl noncommercial openssl php qpl restrictive/distributable ruby ssleay zpl-1}
82    gpl-1 {agpl apache gpl-3 gpl-3+ lgpl-3 lgpl-3+}
83    gpl-2 {agpl apache freetype gpl-3 gpl-3+ lgpl-3 lgpl-3+}
84    gpl-3 {agpl-1 gpl-1 gpl-2}
85    gpl-3+ {agpl-1 gpl-1 gpl-2}
86    gplconflict {agpl cecill gpl}
87    ibmpl {agpl cecill gpl}
88    lgpl-3 {agpl-1 gpl-1 gpl-2}
89    lgpl-3+ {agpl-1 gpl-1 gpl-2}
90    lppl {agpl cecill gpl}
91    mpl {agpl cecill gpl}
92    noncommercial {agpl cecill gpl}
93    openssl {agpl cecill gpl}
94    opensslexception [all_licenses_except openssl ssleay]
95    php {agpl cecill gpl}
96    qpl {agpl cecill gpl}
97    restrictive/distributable {agpl cecill gpl}
98    ruby {agpl cecill gpl}
99    ssleay {agpl cecill gpl}
100    zpl-1 {agpl cecill gpl}"
101
102proc printUsage {} {
103    puts "Usage: $::argv0 \[-hvV\] \[-t macports-tcl-path\] port-name \[variants...\]"
104    puts "  -h    This help"
105    puts "  -t    Give a different location for the base MacPorts Tcl"
106    puts "        file (defaults to /Library/Tcl)"
107    puts "  -v    verbose output"
108    puts "  -V    show version and MacPorts version being used"
109    puts ""
110    puts "port-name is the name of a port to check"
111    puts "variants is the list of variants to enable/disable: +one -two..."
112}
113
114
115# return deps and license for given port
116proc infoForPort {portName variantInfo} {
117    global check_deptypes
118    set dependencyList {}
119    set portSearchResult [mportlookup $portName]
120    if {[llength $portSearchResult] < 1} {
121        puts "Warning: port \"$portName\" not found"
122        return {}
123    }
124    array set portInfo [lindex $portSearchResult 1]
125    set mport [mportopen $portInfo(porturl) [list subport $portName] $variantInfo]
126    array unset portInfo
127    array set portInfo [mportinfo $mport]
128    mportclose $mport
129
130    foreach dependencyType $check_deptypes {
131        if {[info exists portInfo($dependencyType)] && $portInfo($dependencyType) ne ""} {
132            foreach dependency $portInfo($dependencyType) {
133                set afterColon [expr {[string last ":" $dependency] + 1}]
134                lappend dependencyList [string range $dependency $afterColon end]
135            }
136        }
137    }
138
139    set ret [list $dependencyList $portInfo(license)]
140    if {[info exists portInfo(installs_libs)]} {
141        lappend ret $portInfo(installs_libs)
142    } else {
143        # when in doubt, assume code from the dep is incorporated
144        lappend ret yes
145    }
146    if {[info exists portInfo(license_noconflict)]} {
147        lappend ret $portInfo(license_noconflict)
148    }
149    return $ret
150}
151
152# return license with any trailing dash followed by a number and/or plus sign removed
153proc remove_version {license} {
154    set dash [string last - $license]
155    if {$dash != -1 && [regexp {[0-9.+]+} [string range $license [expr {$dash + 1}] end]]} {
156        return [string range $license 0 [expr {$dash - 1}]]
157    } else {
158        return $license
159    }
160}
161
162proc check_licenses {portName variantInfo verbose} {
163    global license_good license_conflicts
164    array set portSeen {}
165    set top_info [infoForPort $portName $variantInfo]
166    if {$top_info == {}} {
167        return 1
168    }
169    set top_license [lindex $top_info 1]
170    foreach noconflict_port [lindex $top_info 3] {
171        set noconflict_ports($noconflict_port) 1
172    }
173    set top_license_names {}
174    # check that top-level port's license(s) are good
175    foreach sublist $top_license {
176        # each element may be a list of alternatives (i.e. only one need apply)
177        set any_good 0
178        set sub_names {}
179        foreach full_lic $sublist {
180            # chop off any trailing version number
181            set lic [remove_version [string tolower $full_lic]]
182            # add name to the list for later
183            lappend sub_names $lic
184            if {[info exists license_good($lic)]} {
185                set any_good 1
186            }
187        }
188        lappend top_license_names $sub_names
189        if {!$any_good} {
190            if {$verbose} {
191                puts "\"$portName\" is not distributable because its license \"$lic\" is not known to be distributable"
192            }
193            return 1
194        }
195    }
196
197    # start with deps of top-level port
198    set portList [lindex $top_info 0]
199    while {[llength $portList] > 0} {
200        set aPort [lindex $portList 0]
201        # mark as seen and remove from the list
202        set portSeen($aPort) 1
203        set portList [lreplace $portList 0 0]
204        if {[info exists noconflict_ports($aPort)]} {
205            continue
206        }
207
208        set aPortInfo [infoForPort $aPort $variantInfo]
209        set aPortLicense [lindex $aPortInfo 1]
210        set installs_libs [lindex $aPortInfo 2]
211        if {!$installs_libs} {
212            continue
213        }
214        foreach sublist $aPortLicense {
215            set any_good 0
216            set any_compatible 0
217            # check that this dependency's license(s) are good
218            foreach full_lic $sublist {
219                set lic [remove_version [string tolower $full_lic]]
220                if {[info exists license_good($lic)]} {
221                    set any_good 1
222                } else {
223                    # no good being compatible with other licenses if it's not distributable itself
224                    continue
225                }
226
227                # ... and that they don't conflict with the top-level port's
228                set any_conflict 0
229                foreach top_sublist [concat $top_license $top_license_names] {
230                    set any_sub_compatible 0
231                    foreach top_lic $top_sublist {
232                        if {![info exists license_conflicts([string tolower $top_lic])]
233                            || ([lsearch -sorted $license_conflicts([string tolower $top_lic]) $lic] == -1
234                            && [lsearch -sorted $license_conflicts([string tolower $top_lic]) [string tolower $full_lic]] == -1)} {
235                            set any_sub_compatible 1
236                            break
237                        }
238                    }
239                    if {!$any_sub_compatible} {
240                        set any_conflict 1
241                        break
242                    }
243                }
244                if {!$any_conflict} {
245                    set any_compatible 1
246                    break
247                }
248            }
249
250            if {!$any_good} {
251                if {$verbose} {
252                    puts "\"$portName\" is not distributable because its dependency \"$aPort\" has license \"$lic\" which is not known to be distributable"
253                }
254                return 1
255            }
256            if {!$any_compatible} {
257                if {$verbose} {
258                    puts "\"$portName\" is not distributable because its license \"$top_lic\" conflicts with license \"$full_lic\" of dependency \"$aPort\""
259                }
260                return 1
261            }
262        }
263
264        # skip deps that are explicitly stated to not conflict
265        array unset aPort_noconflict_ports
266        foreach noconflict_port [lindex $aPortInfo 3] {
267            set aPort_noconflict_ports($noconflict_port) 1
268        }
269        # add its deps to the list
270        foreach possiblyNewPort [lindex $aPortInfo 0] {
271            if {![info exists portSeen($possiblyNewPort)] && ![info exists aPort_noconflict_ports($possiblyNewPort)]} {
272                lappend portList $possiblyNewPort
273            }
274        }
275    }
276
277    if {$verbose} {
278        puts "\"$portName\" is distributable"
279    }
280    return 0
281}
282
283
284# Begin
285
286set macportsTclPath /Library/Tcl
287set verbose 0
288set showVersion 0
289
290while {[string index [lindex $::argv 0] 0] == "-" } {
291    switch [string range [lindex $::argv 0] 1 end] {
292        h {
293            printUsage
294            exit 0
295        }
296        t {
297            if {[llength $::argv] < 2} {
298                puts "-t needs a path"
299                printUsage
300                exit 2
301            }
302            set macportsTclPath [lindex $::argv 1]
303            set ::argv [lrange $::argv 1 end]
304        }
305        v {
306             set verbose 1
307        }
308        V {
309            set showVersion 1
310        }
311        default {
312            puts "Unknown option [lindex $::argv 0]"
313            printUsage
314            exit 2
315        }
316    }
317    set ::argv [lrange $::argv 1 end]
318}
319
320package require macports
321mportinit
322
323if {$showVersion} {
324    puts "Version $MY_VERSION"
325    puts "MacPorts version [macports::version]"
326    exit 0
327}
328
329if {[llength $::argv] == 0} {
330    puts "Error: missing port-name"
331    printUsage
332    exit 2
333}
334set portName [lindex $::argv 0]
335set ::argv [lrange $::argv 1 end]
336
337array set variantInfo {}
338foreach variantSetting $::argv {
339    set flag [string index $variantSetting 0]
340    set variantName [string range $variantSetting 1 end]
341    set variantInfo($variantName) $flag
342}
343
344exit [check_licenses $portName [array get variantInfo] $verbose]
Note: See TracBrowser for help on using the repository browser.