JFIF   ( %!1!%)+...383,7(-.+  ++-+++++-++-++--+--+-+-------+-++-+--+---+++--+7+-+"F!1AQaq"2BRb#3Sr$CDsT&!Q1Aa"2Rbq ?򉄘ǷLR HR,nNb .&W)fJbMOYxj-\bT2(4CQ"qiC/ " %0Jl"e2V  0SDd2@TV^{cW&F͉x9#l,.XɳvRZ C8S 6ml!@!E! `FS!M #(d)Q lml1ml Ų&x(ʨ2NFmj@D<dN5UN˄uTB emLAy#` ` ` I!I 6āHBxL & J#7BQ.$hv h q+tC"EJ) 8R e2U2Y@j%6PF^4LnNBp"8)4JI-ֲvK ^؊)hz[T5˗",Rҥf8ڤS4ʘ!`D ` X+ L,(hl)*S##`6[`0*L T H*HA@I&&r1kr*r*)N$#L  1#ZFSl `[( ("((he`4 Ch [="A R / 0I`twCDcWh"i) cLad\BcLKHZ"ZEW$Ƚ@A~i^`S *A&h:+c Y6vϕGClRPs.`H`(@<$qDe pL@DpLX, E2MP A  `II m& AQ "AT rbg# g2!SiLj*3L \ G;TFL`K BMy 2S`YLh1 d >-"ZfD^Q DH" RAbEV#Lfq,(rETp64-IJ!*p4F$q;G8DQ/TKP2$jp3KW]FtLtƉ1ol]VBgػJH6 )h61GJR7Nj.Z4piJRDd]t]0dP]:N.b'⹙SvDSz]L,_#ugT&[~?cS^"{Bh{/=ۑxOk̳O59o dar793`)SeYM@\ "$E(Tm&)N2Ih)F5EDed(FS,Pa @!@#@lea HCD$11jCLJqcod S3yd*,lL+QEfsgW1nw)cT#dS HXkFJB"6(ʝH)H"#EZh:Y`khݳh%Sc<mlAko2]gDqQtro=3OƸU9_-t8UvW3sGəg*#:c)><"wc\ASmT|6Ę>9~#1Ƈ~ڒE1vVi# I MM#u$8W 5ǍfƬΜg*Qpi1ȩFOf۔S,/⎯(Lrմ`(Z LsbA \6 6dm[I=!r:REI.wgzG)ԇSbӑxuׇTyyL^e'x^ty4Z&eB]I|v59Jjhm;Ng񷫳n<ϞҼѝjk;׹DlY^ҍ\+x9V!j([cmS.NO6jxNζrm&oײizT$N>?~ Sl-:iڥk\at#E!CL`.O0a*w/WV7/r)DŽt7'Nĵ#7O1 ]{[/-2bA<$&Gm_4t)_>)mjG;V^'k59o>ɌM,ؾf9z6 4v_3T.5V/RD-5 %T5XTޫ4TaZ`U *ƱUƲ UG"5+sJJ2E9#܎kr2G3Bb,XM6H: ?@p!'\4V02aԙ) hbZ]:` ev3ʘ'}!ohȒ*TJjr[RFyQ*#{h{R]J]Lr-.D-.җfo$D ?X0%~1P.Og{cWϫ22&Ϭ_V.W3nmiOl}+!˫#`kR33aUb0-g:qmsέ+0HO|&nhOn+}n5QF_"gvLm/z'+r'n_oC语i|1}Gi|}_D~9JZ_%DVQp\koۅjAs~/c0ksUJi^W9W5!>?O:q|ˣSIB/&K<(lg(%Wg$|LW7vߤW߇q|jef3D H\S6(eJb*@&sTKTW/*@v:.N- @ITʓ1Zg&-eꓝM r]EMס{q$b]'7Z7N:O~lNlP7iͲk)$O^퉢<YSD*hr'Z#5e6t[Fdh AJǔP9P 1\R).Il+jI*,(ܢ22N*OwKFX gc?\mB7iA+εe8 "ġ/p5pW-$މ-[a 5ViAW/V{/&UsF./՞ҕ*)rZg.^_+gt_z-oAbqQn*WlHyZ*\TaEewlLR3ԹȭN}MM}aih"5ܕRT$:~'TcT|*)xGC>n+r{XU xuF"<~67у'fxlf`r3D*#Z1ђfH`2dIWo/qB| 63xxW6^m%Kvg>\>x>!H5Nr8J/FJ9Wx(Hou" S'kWاC\9ְ#^OaҮ+~gnkuЉ,aWU*1 읍jnb|e= :2.UL`Q}YS&gI.c=a`%j:C%2@^>])25/ܙ<lzwɛ)ݣS4h3=J tyϬ.E7 8ڞGZu\_JHsݢϑ}IZ"ӳ=X<Ɖ2{a:{7L+>V}c)*lo Yv&+|L;>+/Sj26K+澡*;>-s"}M2] Ig5aCL*r"&\} #^R.7_Mgf}.ߌy(}Z\gP&ʠHj%</{.]rߙQ`>;5g;u6dԛ %xb|oՋTJ5Ϥ(]XqP>f{Jk2,8'~ZU6tMQsg XKg^2ϓ3},[wo۴I|ܷ%[Ol\Pkr]Y//cg6U⧻/VПi8ys_n<\~cze!!H~x;QJZKȮ^ȧG|cS~8ji,Fo+,y~?pk)u /in3JmkX(Mj1N 4c Epc>BO *LfQO&` c;LjcYf 1ɻ)CLsY^Y5" lP/wuEln&dav,(;'W9ej ku`-KHI՟%ԁʁ 1\}?OjsF^Xn$Ё.օC>D:?I @aGE.ĩ1 $ et~T`߸Ir'RX.Zwc%~U=r>-UaFbǺ?R=Z?i'[ASS;siJrzy>nxu$[_B\4}:r'ҵj1_v-[;y?ֹ0I16 . M%4^!S&t ! h !zQð.bBT ?@]?CHq(rd!.$>/x+bnʎNN#w)` )*f!-ɂ\(طYLHzc`Uq7BfCcE0ԉ4Fم쏠ce5T r͸GVlФ?ѣ} mhrkly.Ts㷖)Mө S^%'g>wk%bP[}j~ǾV#K -Fgv켼ǨgɼeSz/6{M=BPZFu\Q75n3Iݤ.W9QfF{vJwF't[@iVj4G~KOnH߿_Do=.c.One?E+GfGN⧭H?4;u`ua|V-+j4?48n ɦ=-]puv&Jc}K>b%U x8pz6L8AXFsW]N55ҦbIWZQ7ï Ԗ3cjz匩ӺOTɖƴ%a'MI}cdR$ݚIζ̝ LIu>J3{^෠㜦˯xܿe\b"2y'x{ RDW b+o2KFhR0:U늞En>լRӉt Iڹ\ wշQEv"v;EJ)yl[5:F0=b4,\PqKtv4{bQz:>C7"8W#Zjdd| cjz%K %Z 9dD{=NFʳAƩtI)kS*s$`:A\ʬ*ֹ9{Nl|eJ١rQnM%z_#x_•TO><)kyD %GN<~y>vfǧB)F)c\lې(#\ h`fgfjTBdhhHL2Y0^ Y0^-"D!QaI15 m~ gՒd|;#gMn(P$l H.R2^PU")pN` N8󫅂OJ;^jz\uumJMF|ηq[]$Vrrt:Q^;QPkHՠ{]HwˆMuIr7!r&- j%"9LtUb56+^TWBqdhHAD7 HwKH^F3LIq #hK`]IWKiH?کǴeԥQ>g{^q^>HKoOB||8aݏS}{S_]ϸ/X~ܵw'OSPAf֩ܟ[>7 @[ֵ;G߇QU*Cթ *OKU^zz[fRnpcJX9u<iq8B]u8 ]I,;[G#2W.¸D8rPG Y%PBJ= wo;PJgx6;yB`3zZGPAͫy{5Nb_re*ONHR]Ji)U{Ӓ:qqɏ[mB4࢒I$ 2vpBADY`DIVAn"Bh$&&cMbdB 鮆wHR'E(ѸZA*H~{B M҅n\@N{7ISCp Vd( r+bg|ns:qg:|J|ɪV.UVaAS͓FyRuLѦT騬 `3􏳕{eo/Tz8DkW?,cl~TqLne֠[B*D +t 6˦S;5KjV3e WBrT.XSHm sl5F%NGM`Y )": J!W4]HTrPX2 QYɕ\m2VLd+`,^ѺiPztUGY6+cӧ6] U%u/ˈFOiB*nFF#ұJ Z/c')?Q͟5.8E~G6e<\?}GkhMFUظOqhEA - "`dQ#(4Ԧf VLmc@q5J8K; M^JZnn)9Zm\ qIJqS: i[9~Oaƒ]Z4F&+666( N]쁼LM(oyvUI/Χ[ھ]hTˉG".SeYgu;hRDtڬv=5 ׁqMS\Ȭi5D]1$*0UL1QY`QdLb[+z9";'yi`OT/4{@EZ'Y0>4I*d nM#5hі.vrM[]Ä;]\ʦS,叕DQZq0fӌI͋]TNK"#;?F;aURx_4WDm+F*0XJE@){ 1R-E2(@Qh l D rT.Q;[J;[`30`ɀ 2#=JeSsxRjG=`H rLJ@ Y$JaB2/x( "Id'6O0CI$:Ol+}I>[L|iK+]ZrH*2Aʶ uHRd)OrrbSx=5dmue1neܬ"e>Lw94勲u ҏ_4GuоJw]QtgSk(qW(6h|v= 1=P/\YZ|R>"*5W/ίR'o %R$5= .!VIRMf4*aR5nv% Usj:V Lj]Bn/TZ&.2„ܒBP)aYRʌW!#ErGf';tW$czI*\KI,c7Zc-ўj|p+-ђ{eg 2;R_{VLM]7sؒFmԻy853gҾqJG!E̤ӏqzs༿? U#R)ŧU(,>,&,-^e^۔.b EW^n<)\9.QeJuFiSh2"EL8yeCKQD\5R,D5.P]c1STt*ZFJ.T:N #%]M}khOe(͓iEMsɆ3( YF<"Ly^*[ry6.ɸm k݊iT%nM8 $Q#F# q 1*?% iS^4oܗ wWPS,aNޖxOxڽqp#F6&o,7LJuMΤK(Td{U Ƹf|q5U{3[FLNK6ӵQY5+'>Q3FSk).&:5z yZq/*q$d+Ge+$lO@Nڤy5eBvˌ䖥shS:JksgksF ꧸oi-FYxy9[Vȼĝ'_.[y2U*c?E+:TsWՀgOS> z75>ncߏ-Kz8ԋ,Ϧ70Z9_1h$Xiu10)0$+$! qsE4wRkh2*T.s%DH:`:=k.'WB{ ȮRGҷ7чVg)CHS}1ݍԳۂ<8g_4y*-Ml\]mZT)mJ~|k<6zWjf4'*u%RNRȉZA) .VLtp 4 V&mtJ#l˅;&{]8>TmhoLXOeD^_J>]jsSej﫦iOM SK([!Vc5zn-A@p]Ӄ \3kmK>#-sܧ?NLar@Js?…Xldny]݌E5•9.8hh69#7js׳R,'pqt:kgPhRԄ+ՕG9}="ֲ\kǁm R73pg$t3+o |o\]'ee5ɐ.7ѐ|ZعSF{qkx5-$Q h5*1yM$ 7)hJ2Kg`-hn*>)EYDIkBpȩAzfǪ>7O K#lߤg]:u~huُ۵u}(mjGIj܏6ES~/5CiRy|kVKGBޭ3;w /jꏈUu>iƪi:WRo'yr4C/?c:w!?\'?#Q:>u/?uEeuG*xY2)?־CAr*23_ץ}գk1%(_ _6aԗ _4 $ϗ+ϫɆzǾIgu?Y<#_xS>i\uɇ۽r}[ͫyRoWCC!H,iD։"Cj5 4] cTk2YZRBvRY~FqQt^RO-g"QP]Ih/t:ljs YӹqI] wqXp KV+8j} uu8PGP&zF:;8+ Sx9(. Q}:ƻWr,Ũ*'shfƧ-6__5,DH{* qp묘G MA}QRe{dyMucǨɾ7߈Avϩe͜jmUi p3\5,ާbf:o+7#ܾ~iU#up=}˄k{NV8m!ҌiptޜBvKi}!ש3UK)`igӞVMR'J[ky~g&6vǍ7ķ>uXd(3瓓[]QTTqnͮz1~_͓k俸0~Z1գ =18cL 5^lf^k^<ҲJɬcC-[^;J8j_q=WpeA_6 4.Ntc>Sv2Jf;G8. 5[,;ArSTˬmpmzjGe EoǩOgDWaGhz<|kT\$Q=u/ci˜S mN&Ok~'0,a} s + NC-G'(*>vw~&*wYG Ŷ K-L/$߮l/A/^:Z@X- Q-D2`@M2+w$Q"胊"47&+Dh'9Y* L7VhT+ -?K]Ik \Ϣgy) s v z)Z ˦2&ލ OjmG9@8F_u䊜r>3K%Yg-FFI]e+Kxkzװy"\Q4Ri'0+P=V&Sw3N/U|UEt*uS c M*tsBE 2ʃ@Kir(˫LRr璜Zy@].%NbXvz덟 hӰNMe#|g͒po9^licxB[e' {U? mlt%?霋ǒxZc X]ϗ15SeE{-Ӕi~DƯO|ë5a@G=%<ƧAs*+tzo, IpȔ|:X6J3Z5JXd]2 3%v*GvE@(S&SX7D0^{5t Z{ﮄsh- ]ɑqEV=^Ki9äBtI@&pEg*O<`F-}ǎ51H,<~qibQѓɳx#l$G9td1U+Sq%B[jOq+^ޏ7K >YY  $KK{*˝e"|$g"6v,,9.DaA,qэI~ܨ|kdv; hz2]x5{M5M~yלqTzUl9Mӏ.WVnkun !jzKO!v|& ;gۇ2BrI閵C tqHe[Zkގ=Q;OԶiᵞBcIU eN cOGz S__>.hNgG6).J$_Taѯ5^LqeB]O?A]H;ò{^0ٺuޚxB|:q'xu4"9Ο7k^eZ_fQOmzm̗{c3ٵKO|m*ek(8"yO(ٵ{LJb2Ǩkgg1_/qrDՆ[_l\ I~Bsc/x ),,̿@PFޞ>O)<<=5m=^x6}~6qoYGޣiY{uN+<,CǚwVxe~c!,5R4u/9In=G•^PF6ɼM򿶤$"\|78ؖYU cXFOKc4s-=6O<;.ϴ޶$q>e? qY}StirX?e/&R'ʑ[ѯMi{?8\g^>\!-VZCf.ȾzRWMh_{^H)mz}V%չM.EJUz7z>ZW6\BW~:W3!S_4~m ǚ! ;VeGKFڵ858Buj:ZZ(/H׭eav!$gpLV)țAJO~YBꤞ厅XJdjg{hR9~_f '5U+}W5%ZjzgTtozYD @%JK\qymeЪKIIp"xoz\B1$G)8Ԅ Jeyc".yyVBR-%BEA-k^Luj cYwԄ%X!e-4ZRḡlJvYsB԰˗0?RM\TlaߏVu4BmY!UyYylgd!m2$i=[hN,6)_~7͖CDF2zÕ{?l;Hܲk׋!/XAłrCXEI{]P[e! ?%Ktqܱ5! jַĞ*TvAG)fuxTҖV7~ 4=r! ob%jTwU$Bnqed䤿@0P&V]HJ)^YrޯĿbsY8=1! n}UD*7uƫi~!s[W{V9J;~Ӯ|[3s۷dڔIj?qJ'O,IkE]G(5\ۖ7)-g,ŶǗ=~e>k쐁%(g˦o[fxN_baGBm:܆VGЗ,G_D!/og,ҢVܤ_iS_~@ SkidSec Webshell

SkidSec WebShell

Server Address : 172.31.38.4

Web Server : Apache/2.4.58 (Ubuntu)

Uname : Linux ip-172-31-38-4 6.14.0-1017-aws #17~24.04.1-Ubuntu SMP Wed Nov 5 10:48:17 UTC 2025 x86_64

PHP Version : 7.4.33



Current Path : /snap/core/current/usr/share/dh-python/



Current File : //snap/core/current/usr/share/dh-python/pybuild
#! /usr/bin/python3
# vim: et ts=4 sw=4
# Copyright © 2012-2013 Piotr Ożarowski <piotr@debian.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import logging
import argparse
import sys
from os import environ, getcwd, makedirs, remove
from os.path import abspath, exists, join
from shutil import rmtree

logging.basicConfig(format='%(levelname).1s: pybuild '
                           '%(module)s:%(lineno)d: %(message)s')
log = logging.getLogger('dhpython')


def main(cfg):
    log.debug('cfg: %s', cfg)
    from dhpython import build
    from dhpython.version import Version, build_sorted, get_requested_versions
    from dhpython.interpreter import Interpreter
    from dhpython.tools import execute, move_matching_files

    if cfg.list_systems:
        for name, Plugin in sorted(build.plugins.items()):
            print(name, '\t', Plugin.DESCRIPTION)
        exit(0)

    nocheck = False
    if 'DEB_BUILD_OPTIONS' in environ:
        nocheck = 'nocheck' in environ['DEB_BUILD_OPTIONS']

    env = environ.copy()
    if 'LC_ALL' not in env:
        env['LC_ALL'] = 'C.UTF-8'
    if 'no_proxy' not in env:
        env['no_proxy'] = 'localhost'
    if 'http_proxy' not in env:
        env['http_proxy'] = 'http://127.0.0.1:9/'
    elif not env['http_proxy']:
        del env['http_proxy']  # some tools don't like empty var.
    if 'https_proxy' not in env:
        env['https_proxy'] = 'https://127.0.0.1:9/'
    elif not env['https_proxy']:
        del env['https_proxy']  # some tools don't like empty var.

    if cfg.system:
        certainty = 99
        Plugin = build.plugins.get(cfg.system)
        if not Plugin:
            log.error('unrecognized build system: %s', cfg.system)
            exit(10)
        plugin = Plugin(cfg)
        context = {'ENV': env, 'args': {}, 'dir': cfg.dir}
        plugin.detect(context)
    else:
        plugin, certainty, context = None, 0, None
        for Plugin in build.plugins.values():
            try:
                tmp_plugin = Plugin(cfg)
            except Exception as err:
                log.warn('cannot initialize %s plugin: %s', Plugin.NAME,
                         err, exc_info=cfg.verbose)
                continue
            tmp_context = {'ENV': env, 'args': {}, 'dir': cfg.dir}
            tmp_certainty = tmp_plugin.detect(tmp_context)
            if tmp_certainty and tmp_certainty > certainty:
                plugin, certainty, context = tmp_plugin, tmp_certainty, tmp_context
        del Plugin
        if not plugin:
            log.error('cannot detect build system, please use --system option'
                      ' or set PYBUILD_SYSTEM env. variable')
            exit(11)
    for interpreter in cfg.interpreter:
        if plugin.SUPPORTED_INTERPRETERS is not True and interpreter not in plugin.SUPPORTED_INTERPRETERS:
            log.error('interpreter %s not supported by %s', interpreter, plugin)
            exit(12)
    log.debug('detected build system: %s (certainty: %s%%)', plugin.NAME, certainty)

    if cfg.detect_only:
        if not cfg.really_quiet:
            print(plugin.NAME)
        exit(0)

    # reversed so that default Python version will be last
    versions = cfg.versions
    if not versions:
        log.debug('defaulting to all supported Python 3.X versions')
        versions = list(get_requested_versions('cpython3', available=True))
    versions = [Version(v) for v in versions]

    def get_option(name, interpreter=None, version=None, default=None):
        if interpreter:
            # try PYBUILD_NAME_python3.3-dbg (or hardcoded interpreter)
            i = interpreter.format(version=version or '')
            opt = "PYBUILD_{}_{}".format(name.upper(), i)
            if opt in environ:
                return environ[opt]
            # try PYBUILD_NAME_python3-dbg (if not checked above)
            if '{version}' in interpreter and version:
                i = interpreter.format(version=version.major)
                opt = "PYBUILD_{}_{}".format(name.upper(), i)
                if opt in environ:
                    return environ[opt]
        # try PYBUILD_NAME
        opt = "PYBUILD_{}".format(name.upper())
        if opt in environ:
            return environ[opt]
        # try command line args
        return getattr(cfg, name, default) or default

    def get_args(context, step, version, interpreter):
        i = interpreter.format(version=version)
        home_dir = '.pybuild/{}_{}'.format(interpreter.format(version='X.Y'), version)
        build_dir = get_option('build_dir', interpreter, version,
                               default=join(home_dir, 'build'))

        ipreter = Interpreter(i)
        destdir = context['destdir'].format(version=version, interpreter=i)
        if cfg.name:
            package = ipreter.suggest_pkg_name(cfg.name)
        else:
            package = 'PYBUILD_NAME_not_set'
        if cfg.name and destdir.rstrip('/').endswith('debian/tmp'):
            destdir = "debian/{}".format(package)
        destdir = abspath(destdir)

        args = dict(context['args'])
        args.update({
            'package': package,
            'interpreter': ipreter,
            'version': version,
            'args': get_option("%s_args" % step, interpreter, version, ''),
            'dir': abspath(context['dir'].format(version=version, interpreter=i)),
            'destdir': destdir,
            'build_dir': abspath(build_dir.format(version=version, interpreter=i)),
            # versioned dist-packages even for Python 3.X - dh_python3 will fix it later
            # (and will have a chance to compare files)
            'install_dir': get_option('install_dir', interpreter, version,
                                      '/usr/lib/python{version}/dist-packages'
                                      ).format(version=version, interpreter=i),
            'home_dir': abspath(home_dir)})
        if interpreter == 'pypy':
            args['install_dir'] = '/usr/lib/pypy/dist-packages/'
        if step in {'build', 'test'}:
            pp = context['ENV'].get('PYTHONPATH')
            pp = pp.split(':') if pp else []
            if step == 'test':
                args['test_dir'] = join(args['destdir'], args['install_dir'].lstrip('/'))
                if args['test_dir'] not in pp:
                    pp.append(args['test_dir'])
            if args['build_dir'] not in pp:
                pp.append(args['build_dir'])
            args['PYTHONPATH'] = ':'.join(pp)

        if not exists(args['build_dir']):
            makedirs(args['build_dir'])

        return args

    def is_disabled(step, interpreter, version):
        i = interpreter
        prefix = "{}/".format(step)
        disabled = (get_option('disable', i, version) or '').split()
        for item in disabled:
            if item in (step, '1'):
                log.debug('disabling {} step for {} {}'.format(step, i, version))
                return True
            if item.startswith(prefix):
                disabled.append(item[len(prefix):])
        if i in disabled or str(version) in disabled or \
                i.format(version=version) in disabled or \
                i.format(version=version.major) in disabled:
            log.debug('disabling {} step for {} {}'.format(step, i, version))
            return True
        return False

    def run(func, interpreter, version, context):
        step = func.__func__.__name__
        args = get_args(context, step, version, interpreter)
        if 'PYTHONPATH' in args:
            env = dict(context['ENV'])
            env['PYTHONPATH'] = args['PYTHONPATH']
        else:
            env = context['ENV']

        before_cmd = get_option('before_{}'.format(step), interpreter, version)
        if before_cmd:
            if cfg.quiet:
                log_file = join(args['home_dir'], 'before_{}_cmd.log'.format(step))
            else:
                log_file = False
            command = before_cmd.format(**args)
            output = execute(command, context['dir'], env, log_file)
            if output['returncode'] != 0:
                msg = 'exit code={}: {}'.format(output['returncode'], command)
                raise Exception(msg)

        fpath = join(args['home_dir'], 'build_dirs_to_rm_before_install')
        if step == 'install' and exists(fpath):
            with open(fpath) as fp:
                for line in fp:
                    dpath = join(args['build_dir'], line.strip('\n'))
                    if exists(dpath):
                        rmtree(dpath)
            remove(fpath)
        result = func(context, args)

        after_cmd = get_option('after_{}'.format(step), interpreter, version)
        if after_cmd:
            if cfg.quiet:
                log_file = join(args['home_dir'], 'after_{}_cmd.log'.format(step))
            else:
                log_file = False
            command = after_cmd.format(**args)
            output = execute(command, context['dir'], env, log_file)
            if output['returncode'] != 0:
                msg = 'exit code={}: {}'.format(output['returncode'], command)
                raise Exception(msg)
        return result

    func = None
    if cfg.clean_only:
        func = plugin.clean
    elif cfg.configure_only:
        func = plugin.configure
    elif cfg.build_only:
        func = plugin.build
    elif cfg.install_only:
        func = plugin.install
    elif cfg.test_only:
        func = plugin.test

    ### one function for each interpreter at a time mode ###
    if func:
        step = func.__func__.__name__
        if step == 'test' and nocheck:
            exit(0)
        for i in cfg.interpreter:
            ipreter = Interpreter(interpreter.format(version=versions[0]))
            iversions = build_sorted(versions, impl=ipreter.impl)
            if '{version}' not in i and len(versions) > 1:
                log.info('limiting Python versions to %s due to missing {version}'
                         ' in interpreter string', str(versions[-1]))
                iversions = versions[-1:]  # just the default or closest to default
            for version in iversions:
                if is_disabled(step, i, version):
                    continue
                c = dict(context)
                c['dir'] = get_option('dir', i, version, cfg.dir)
                c['destdir'] = get_option('destdir', i, version, cfg.destdir)
                try:
                    run(func, i, version, c)
                except Exception as err:
                    log.error('%s: plugin %s failed with: %s',
                              step, plugin.NAME, err, exc_info=cfg.verbose)
                    exit(13)
                if step == 'install':
                    ext_destdir = get_option('ext_destdir', i, version)
                    if ext_destdir:
                        move_matching_files(c['destdir'], ext_destdir,
                                            get_option('ext_pattern', i, version))
        exit(0)

    ### all functions for interpreters in batches mode ###
    try:
        context_map = {}
        for i in cfg.interpreter:
            ipreter = Interpreter(interpreter.format(version=versions[0]))
            iversions = build_sorted(versions, impl=ipreter.impl)
            if '{version}' not in i and len(versions) > 1:
                log.info('limiting Python versions to %s due to missing {version}'
                         ' in interpreter string', str(versions[-1]))
                iversions = versions[-1:]  # just the default or closest to default
            for version in iversions:
                key = (i, version)
                if key in context_map:
                    c = context_map[key]
                else:
                    c = dict(context)
                    c['dir'] = get_option('dir', i, version, cfg.dir)
                    c['destdir'] = get_option('destdir', i, version, cfg.destdir)
                    context_map[key] = c

                if not is_disabled('clean', i, version):
                    run(plugin.clean, i, version, c)
                if not is_disabled('configure', i, version):
                    run(plugin.configure, i, version, c)
                if not is_disabled('build', i, version):
                    run(plugin.build, i, version, c)
                if not is_disabled('install', i, version):
                    run(plugin.install, i, version, c)
                    ext_destdir = get_option('ext_destdir', i, version)
                    if ext_destdir:
                        move_matching_files(c['destdir'], ext_destdir,
                                            get_option('ext_pattern', i, version))
                if not nocheck and not is_disabled('test', i, version):
                    run(plugin.test, i, version, c)
    except Exception as err:
        log.error('plugin %s failed: %s', plugin.NAME, err,
                  exc_info=cfg.verbose)
        exit(14)


def parse_args(argv):
    usage = '%(prog)s [ACTION] [BUILD SYSTEM ARGS] [DIRECTORIES] [OPTIONS]'
    parser = argparse.ArgumentParser(usage=usage)
    parser.add_argument('-v', '--verbose', action='store_true',
                        default=environ.get('PYBUILD_VERBOSE') == '1',
                        help='turn verbose mode on')
    parser.add_argument('-q', '--quiet', action='store_true',
                        default=environ.get('PYBUILD_QUIET') == '1',
                        help='doesn\'t show external command\'s output')
    parser.add_argument('-qq', '--really-quiet', action='store_true',
                        default=environ.get('PYBUILD_RQUIET') == '1',
                        help='be quiet')
    parser.add_argument('--version', action='version', version='%(prog)s 2.20151103ubuntu1.2')

    action = parser.add_argument_group('ACTION', '''The default is to build,
        install and test the library using detected build system version by
        version. Selecting one of following actions, will invoke given action
        for all versions - one by one - which (contrary to the default action)
        in some build systems can overwrite previous results.''')
    action.add_argument('--detect', action='store_true', dest='detect_only',
                        help='return the name of detected build system')
    action.add_argument('--clean', action='store_true', dest='clean_only',
                        help='clean files using auto-detected build system specific methods')
    action.add_argument('--configure', action='store_true', dest='configure_only',
                        help='invoke configure step for all requested Python versions')
    action.add_argument('--build', action='store_true', dest='build_only',
                        help='invoke build step for all requested Python versions')
    action.add_argument('--install', action='store_true', dest='install_only',
                        help='invoke install step for all requested Python versions')
    action.add_argument('--test', action='store_true', dest='test_only',
                        help='invoke tests for auto-detected build system')
    action.add_argument('--list-systems', action='store_true',
                        help='list available build systems and exit')

    arguments = parser.add_argument_group('BUILD SYSTEM ARGS', '''
        Additional arguments passed to the build system.
        --system=custom requires complete command.''')
    arguments.add_argument('--before-clean', metavar='CMD',
                           help='invoked before the clean command')
    arguments.add_argument('--clean-args', metavar='ARGS')
    arguments.add_argument('--after-clean', metavar='CMD',
                           help='invoked after the clean command')

    arguments.add_argument('--before-configure', metavar='CMD',
                           help='invoked before the configure command')
    arguments.add_argument('--configure-args', metavar='ARGS')
    arguments.add_argument('--after-configure', metavar='CMD',
                           help='invoked after the configure command')

    arguments.add_argument('--before-build', metavar='CMD',
                           help='invoked before the build command')
    arguments.add_argument('--build-args', metavar='ARGS')
    arguments.add_argument('--after-build', metavar='CMD',
                           help='invoked after the build command')

    arguments.add_argument('--before-install', metavar='CMD',
                           help='invoked before the install command')
    arguments.add_argument('--install-args', metavar='ARGS')
    arguments.add_argument('--after-install', metavar='CMD',
                           help='invoked after the install command')

    arguments.add_argument('--before-test', metavar='CMD',
                           help='invoked before the test command')
    arguments.add_argument('--test-args', metavar='ARGS')
    arguments.add_argument('--after-test', metavar='CMD',
                           help='invoked after the test command')

    tests = parser.add_argument_group('TESTS', '''\
        unittest\'s discover is used by default (if available)''')
    tests.add_argument('--test-nose', action='store_true',
                       default=environ.get('PYBUILD_TEST_NOSE') == '1',
                       help='use nose module in --test step')
    tests.add_argument('--test-pytest', action='store_true',
                       default=environ.get('PYBUILD_TEST_PYTEST') == '1',
                       help='use pytest module in --test step')
    tests.add_argument('--test-tox', action='store_true',
                       default=environ.get('PYBUILD_TEST_TOX') == '1',
                       help='use tox in --test step')

    dirs = parser.add_argument_group('DIRECTORIES')
    dirs.add_argument('-d', '--dir', action='store', metavar='DIR',
                      default=getcwd(),
                      help='source files directory - base for other relative dirs [default: CWD]')
    dirs.add_argument('--dest-dir', action='store', metavar='DIR', dest='destdir',
                      default=environ.get('DESTDIR', 'debian/tmp'),
                      help='destination directory [default: debian/tmp]')
    dirs.add_argument('--ext-dest-dir', action='store', metavar='DIR', dest='ext_destdir',
                      default=environ.get('EXT_DESTDIR'),
                      help='destination directory for .so files')
    dirs.add_argument('--ext-pattern', action='store', metavar='PATTERN',
                      default=environ.get('EXT_PATTERN', r'\.so(\.[^/]*)?$'),
                      help='regular expression for files that should be moved'
                      ' if --ext-dest-dir is set [default: .so files]')
    dirs.add_argument('--install-dir', action='store', metavar='DIR',
                      help='installation directory [default: .../dist-packages]')
    dirs.add_argument('--name', action='store',
                      default=environ.get('PYBUILD_NAME'),
                      help='use this name to guess destination directories')

    limit = parser.add_argument_group('LIMITATIONS')
    limit.add_argument('-s', '--system',
                       default=environ.get('PYBUILD_SYSTEM'),
                       help='select a build system [default: auto-detection]')
    limit.add_argument('-p', '--pyver', action='append', dest='versions',
                       help='''build for Python VERSION.
                               This option can be used multiple times
                               [default: all supported Python 3.X versions]''')
    limit.add_argument('-i', '--interpreter', action='append',
                       help='change interpreter [default: python{version}]')
    limit.add_argument('--disable', metavar='ITEMS',
                       help='disable action, interpreter or version')

    args = parser.parse_args()
    if not args.interpreter:
        args.interpreter = environ.get('PYBUILD_INTERPRETERS', 'python{version}').split()
    if not args.versions:
        args.versions = environ.get('PYBUILD_VERSIONS', '').split()
    else:
        # add support for -p `pyversions -rv`
        versions = []
        for version in args.versions:
            versions.extend(version.split())
        args.versions = versions

    if args.test_nose or args.test_pytest or args.test_tox\
       or args.system == 'custom':
        args.custom_tests = True
    else:
        args.custom_tests = False

    return args


if __name__ == '__main__':
    cfg = parse_args(sys.argv)
    if cfg.really_quiet:
        cfg.quiet = True
        log.setLevel(logging.CRITICAL)
    elif cfg.verbose:
        log.setLevel(logging.DEBUG)
    else:
        log.setLevel(logging.INFO)
    log.debug('version: 2.20151103ubuntu1.2')
    log.debug(sys.argv)
    main(cfg)
    # let dh/cdbs clean the .pybuild dir
    # rmtree(join(cfg.dir, '.pybuild'))