xpra icon
Bug tracker and wiki

Changes in trunk/src/setup.py [6000:22183] in xpra


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r22183  
    22
    33# This file is part of Xpra.
    4 # Copyright (C) 2010-2014 Antoine Martin <antoine@devloop.org.uk>
     4# Copyright (C) 2010-2018 Antoine Martin <antoine@xpra.org>
    55# Copyright (C) 2008, 2009, 2010 Nathaniel Smith <njs@pobox.com>
    66# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     
    1111# does the make_constants hack.)
    1212
    13 import commands
     13import ssl
     14import sys
    1415import glob
     16import shutil
     17import os.path
     18
    1519from distutils.core import setup
    1620from distutils.extension import Extension
    17 import subprocess, sys, traceback
    18 import os.path
    19 import stat
     21from distutils.command.build import build
     22from distutils.command.install_data import install_data
     23
     24import xpra
     25from xpra.os_util import (
     26    get_status_output, getUbuntuVersion,
     27    PYTHON3, BITS, WIN32, OSX, LINUX, POSIX, NETBSD, FREEBSD, OPENBSD,
     28    is_Ubuntu, is_Debian, is_Raspbian, is_Fedora, is_CentOS,
     29    )
     30
     31if sys.version<'2.7':
     32    raise Exception("xpra no longer supports Python 2 versions older than 2.7")
     33if sys.version[0]=='3' and sys.version<'3.4':
     34    raise Exception("xpra no longer supports Python 3 versions older than 3.4")
     35#we don't support versions of Python without the new ssl code:
     36if not hasattr(ssl, "SSLContext"):
     37    print("Warning: xpra requires a Python version with ssl.SSLContext support")
     38    print(" SSL support will not be available!")
    2039
    2140print(" ".join(sys.argv))
    2241
    23 import xpra
    24 from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
    25 
    26 WIN32 = sys.platform.startswith("win")
    27 OSX = sys.platform.startswith("darwin")
    28 
    29 
     42#*******************************************************************************
     43# build options, these may get modified further down..
     44#
     45data_files = []
     46modules = []
     47packages = []       #used by py2app
     48excludes = []       #only used by cx_freeze on win32
     49ext_modules = []
     50cmdclass = {}
     51scripts = []
     52description = "multi-platform screen and application forwarding system"
     53long_description = "Xpra is a multi platform persistent remote display server and client for " + \
     54            "forwarding applications and desktop screens. Also known as 'screen for X11'."
     55url = "http://xpra.org/"
     56
     57
     58XPRA_VERSION = xpra.__version__         #@UndefinedVariable
     59setup_options = {
     60                 "name"             : "xpra",
     61                 "version"          : XPRA_VERSION,
     62                 "license"          : "GPLv2+",
     63                 "author"           : "Antoine Martin",
     64                 "author_email"     : "antoine@xpra.org",
     65                 "url"              : url,
     66                 "download_url"     : "http://xpra.org/src/",
     67                 "description"      : description,
     68                 "long_description" : long_description,
     69                 "data_files"       : data_files,
     70                 "py_modules"       : modules,
     71                 }
     72
     73
     74if "pkg-info" in sys.argv:
     75    with open("PKG-INFO", "wb") as f:
     76        pkg_info_values = setup_options.copy()
     77        pkg_info_values.update({
     78                                "metadata_version"  : "1.1",
     79                                "summary"           :  description,
     80                                "home_page"         : url,
     81                                })
     82        for k in ("Metadata-Version", "Name", "Version", "Summary", "Home-page",
     83                  "Author", "Author-email", "License", "Download-URL", "Description"):
     84            v = pkg_info_values[k.lower().replace("-", "_")]
     85            f.write(b"%s: %s\n" % (k, v))
     86    sys.exit(0)
     87
     88
     89print("Xpra version %s" % XPRA_VERSION)
    3090#*******************************************************************************
    3191# Most of the options below can be modified on the command line
     
    3393# only the default values are specified here:
    3494#*******************************************************************************
     95
     96PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     97has_pkg_config = False
     98if PKG_CONFIG:
     99    v = get_status_output([PKG_CONFIG, "--version"])
     100    has_pkg_config = v[0]==0 and v[1]
     101    if has_pkg_config:
     102        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     103    else:
     104        print("WARNING: pkg-config not found!")
     105
     106for arg in list(sys.argv):
     107    if arg.startswith("--pkg-config-path="):
     108        pcp = arg[len("--pkg-config-path="):]
     109        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     110        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     111        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     112        sys.argv.remove(arg)
     113
     114def no_pkgconfig(*_pkgs_options, **_ekw):
     115    return {}
     116
    35117def pkg_config_ok(*args):
    36     return commands.getstatusoutput("pkg-config %s" % (" ".join(args)))[0]==0
    37 
    38 shadow_ENABLED = SHADOW_SUPPORTED
    39 server_ENABLED = LOCAL_SERVERS_SUPPORTED or shadow_ENABLED
    40 client_ENABLED = True
    41 
    42 x11_ENABLED = not WIN32 and not OSX
    43 argb_ENABLED = True
    44 gtk2_ENABLED = client_ENABLED
    45 gtk3_ENABLED = False
    46 qt4_ENABLED = False
    47 opengl_ENABLED = client_ENABLED
    48 html5_ENABLED = not WIN32 and not OSX
    49 
    50 bencode_ENABLED         = True
    51 cython_bencode_ENABLED  = True
    52 rencode_ENABLED         = True
    53 cymaths_ENABLED         = True
    54 cyxor_ENABLED           = True
    55 clipboard_ENABLED       = True
    56 Xdummy_ENABLED          = None          #none means auto-detect
    57 sound_ENABLED           = True
    58 
    59 enc_proxy_ENABLED       = True
    60 enc_x264_ENABLED        = True          #too important to detect
    61 enc_x265_ENABLED        = pkg_config_ok("--exists", "x265")
    62 webp_ENABLED            = WIN32 or pkg_config_ok("--atleast-version=0.3", "libwebp")
    63 x264_static_ENABLED     = False
    64 x265_static_ENABLED     = False
    65 vpx_ENABLED             = WIN32 or pkg_config_ok("--atleast-version=1.0", "vpx") or pkg_config_ok("--atleast-version=1.0", "libvpx")
    66 vpx_static_ENABLED      = False
    67 #ffmpeg 1.x and libav:
    68 dec_avcodec_ENABLED     = not WIN32 and pkg_config_ok("--max-version=55", "libavcodec")
    69 #ffmpeg 2 onwards:
    70 dec_avcodec2_ENABLED    = WIN32 or pkg_config_ok("--atleast-version=55", "libavcodec")
    71 # some version strings I found:
    72 # Fedora 19: 54.92.100
    73 # Fedora 20: 55.39.101
    74 # Debian sid and jessie: 54.35.0
    75 # Debian wheezy: 53.35
    76 avcodec_static_ENABLED  = False
    77 avcodec2_static_ENABLED = False
    78 csc_swscale_ENABLED     = WIN32 or pkg_config_ok("--exists", "libswscale")
    79 swscale_static_ENABLED  = False
    80 csc_cython_ENABLED      = True
    81 webm_ENABLED            = True
    82 nvenc_ENABLED           = pkg_config_ok("--exists", "nvenc3")
    83 csc_nvcuda_ENABLED      = pkg_config_ok("--exists", "cuda")
    84 csc_opencl_ENABLED      = pkg_config_ok("--exists", "OpenCL")
    85 
     118    return get_status_output([PKG_CONFIG] + [str(x) for x in args])[0]==0
     119
     120def pkg_config_version(req_version, pkgname):
     121    cmd = [PKG_CONFIG, "--modversion", pkgname]
     122    r, out, _ = get_status_output(cmd)
     123    if r!=0 or not out:
     124        return False
     125    from distutils.version import LooseVersion
     126    return LooseVersion(out)>=LooseVersion(req_version)
     127
     128def is_RH():
     129    try:
     130        with open("/etc/redhat-release", mode='rb') as f:
     131            data = f.read()
     132        return data.startswith("CentOS") or data.startswith("RedHat")
     133    except:
     134        pass
     135    return False
     136
     137DEFAULT = True
     138if "--minimal" in sys.argv:
     139    sys.argv.remove("--minimal")
     140    DEFAULT = False
     141
     142from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     143shadow_ENABLED = SHADOW_SUPPORTED and DEFAULT
     144server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and DEFAULT
     145rfb_ENABLED = server_ENABLED
     146service_ENABLED = LINUX and server_ENABLED
     147sd_listen_ENABLED = POSIX and pkg_config_ok("--exists", "libsystemd") and (not is_Ubuntu() or getUbuntuVersion()>(16, 4))
     148proxy_ENABLED  = DEFAULT
     149client_ENABLED = DEFAULT
     150scripts_ENABLED = not WIN32
     151cython_ENABLED = DEFAULT
     152modules_ENABLED = DEFAULT
     153data_ENABLED = DEFAULT
     154
     155x11_ENABLED = DEFAULT and not WIN32 and not OSX
     156xinput_ENABLED = x11_ENABLED
     157uinput_ENABLED = x11_ENABLED
     158dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     159gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     160gtk2_ENABLED = DEFAULT and client_ENABLED and not PYTHON3
     161gtk3_ENABLED = DEFAULT and client_ENABLED and PYTHON3
     162opengl_ENABLED = DEFAULT and client_ENABLED
     163html5_ENABLED = DEFAULT
     164html5_gzip_ENABLED = DEFAULT
     165html5_brotli_ENABLED = DEFAULT
     166minify_ENABLED = html5_ENABLED
     167pam_ENABLED = DEFAULT and (server_ENABLED or proxy_ENABLED) and POSIX and not OSX and (os.path.exists("/usr/include/pam/pam_misc.h") or os.path.exists("/usr/include/security/pam_misc.h"))
     168
     169xdg_open_ENABLED        = LINUX and DEFAULT
     170netdev_ENABLED          = LINUX and DEFAULT
     171vsock_ENABLED           = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h")
     172bencode_ENABLED         = DEFAULT
     173cython_bencode_ENABLED  = DEFAULT
     174clipboard_ENABLED       = DEFAULT
     175Xdummy_ENABLED          = None if POSIX else False  #None means auto-detect
     176Xdummy_wrapper_ENABLED  = None if POSIX else False  #None means auto-detect
     177if WIN32 or OSX:
     178    Xdummy_ENABLED = False
     179sound_ENABLED           = DEFAULT
     180printing_ENABLED        = DEFAULT
     181crypto_ENABLED          = DEFAULT
     182mdns_ENABLED            = DEFAULT
     183websockets_ENABLED      = DEFAULT
     184
     185enc_proxy_ENABLED       = DEFAULT
     186enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264")
     187#crashes on 32-bit windows:
     188enc_x265_ENABLED        = (not WIN32) and pkg_config_ok("--exists", "x265")
     189pillow_ENABLED          = DEFAULT
     190webp_ENABLED            = DEFAULT and pkg_config_version("0.5", "libwebp")
     191jpeg_encoder_ENABLED    = DEFAULT and pkg_config_version("1.2", "libturbojpeg")
     192jpeg_decoder_ENABLED    = DEFAULT and pkg_config_version("1.4", "libturbojpeg")
     193vpx_ENABLED             = DEFAULT and pkg_config_version("1.4", "vpx")
     194enc_ffmpeg_ENABLED      = DEFAULT and pkg_config_version("58.18", "libavcodec")
     195#opencv currently broken on 32-bit windows (crashes on load):
     196webcam_ENABLED          = DEFAULT and not OSX and (not WIN32 or BITS==64)
     197notifications_ENABLED   = DEFAULT
     198keyboard_ENABLED        = DEFAULT
     199v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not FREEBSD and not OPENBSD)
     200#ffmpeg 3.1 or later is required
     201dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("57", "libavcodec")
     202csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale")
     203nvenc_ENABLED = DEFAULT and BITS==64 and pkg_config_version("7", "nvenc")
     204nvfbc_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvfbc")
     205cuda_kernels_ENABLED    = DEFAULT
     206cuda_rebuild_ENABLED    = DEFAULT
     207csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv")
     208example_ENABLED         = DEFAULT
     209
     210#Cython / gcc / packaging build options:
     211annotate_ENABLED        = DEFAULT
    86212warn_ENABLED            = True
    87213strict_ENABLED          = True
    88 PIC_ENABLED             = True
     214PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89215debug_ENABLED           = False
    90216verbose_ENABLED         = False
    91217bundle_tests_ENABLED    = False
     218tests_ENABLED           = False
     219rebuild_ENABLED         = "--skip-build" not in sys.argv
    92220
    93221#allow some of these flags to be modified on the command line:
    94 SWITCHES = ("enc_x264", "x264_static",
    95             "enc_x265", "x265_static",
    96             "nvenc",
    97             "dec_avcodec", "avcodec_static",
    98             "dec_avcodec2", "avcodec2_static",
    99             "csc_swscale", "swscale_static",
    100             "csc_nvcuda", "csc_opencl", "csc_cython",
    101             "vpx", "vpx_static",
    102             "webp", "webm",
    103             "rencode", "bencode", "cython_bencode",
    104             "clipboard",
    105             "server", "client", "x11",
    106             "gtk2", "gtk3", "qt4", "html5",
    107             "sound", "cyxor", "cymaths", "opengl", "argb",
    108             "warn", "strict", "shadow", "debug", "PIC", "Xdummy", "verbose", "bundle_tests")
     222SWITCHES = [
     223    "cython", "modules", "data",
     224    "enc_x264", "enc_x265", "enc_ffmpeg",
     225    "nvenc", "cuda_kernels", "cuda_rebuild", "nvfbc",
     226    "vpx", "webp", "pillow", "jpeg_encoder", "jpeg_decoder",
     227    "v4l2",
     228    "dec_avcodec2", "csc_swscale",
     229    "csc_libyuv",
     230    "bencode", "cython_bencode", "vsock", "netdev", "mdns",
     231    "clipboard",
     232    "scripts",
     233    "server", "client", "dbus", "x11", "xinput", "uinput", "sd_listen",
     234    "gtk_x11", "service",
     235    "gtk2", "gtk3", "example",
     236    "html5", "minify", "html5_gzip", "html5_brotli",
     237    "pam", "xdg_open",
     238    "sound", "opengl", "printing", "webcam", "notifications", "keyboard",
     239    "rebuild",
     240    "annotate", "warn", "strict",
     241    "shadow", "proxy", "rfb",
     242    "debug", "PIC",
     243    "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests",
     244    ]
    109245HELP = "-h" in sys.argv or "--help" in sys.argv
    110246if HELP:
     
    120256            default_str = "auto-detect"
    121257        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     258    print("  --pkg-config-path=PATH")
     259    print("  --rpath=PATH")
    122260    sys.exit(0)
    123261
     262install = None
     263rpath = None
     264ssl_cert = None
     265ssl_key = None
     266minifier = None
     267share_xpra = None
    124268filtered_args = []
    125269for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     270    matched = False
     271    for x in ("rpath", "ssl-cert", "ssl-key", "install", "share-xpra"):
     272        varg = "--%s=" % x
     273        if arg.startswith(varg):
     274            value = arg[len(varg):]
     275            globals()[x.replace("-", "_")] = value
     276            #remove these arguments from sys.argv,
     277            #except for --install=PATH
     278            matched = x!="install"
     279            break
     280    if matched:
    129281        continue
    130     matched = False
    131282    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     283        with_str = "--with-%s" % x
     284        without_str = "--without-%s" % x
     285        if arg.startswith(with_str+"="):
     286            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     287            matched = True
     288            break
     289        elif arg==with_str:
    133290            vars()["%s_ENABLED" % x] = True
    134291            matched = True
    135292            break
    136         elif arg=="--without-%s" % x:
     293        elif arg==without_str:
    137294            vars()["%s_ENABLED" % x] = False
    138295            matched = True
     
    145302    for x in SWITCHES:
    146303        switches_info[x] = vars()["%s_ENABLED" % x]
    147     print("build switches: %s" % switches_info)
    148     if LOCAL_SERVERS_SUPPORTED:
    149         print("Xdummy build flag: %s" % Xdummy_ENABLED)
    150 
     304    print("build switches:")
     305    for k in sorted(SWITCHES):
     306        v = switches_info[k]
     307        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
     308
     309    if (enc_ffmpeg_ENABLED or enc_x264_ENABLED or enc_x265_ENABLED or
     310        nvenc_ENABLED or OSX or x11_ENABLED):
     311        assert cython_ENABLED
    151312    #sanity check the flags:
    152313    if clipboard_ENABLED and not server_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    153314        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154315        clipboard_ENABLED = False
    155     if opengl_ENABLED and not gtk2_ENABLED:
    156         print("Warning: opengl can only be used with the gtk2 clients")
    157         opengl_ENABLED = False
    158     if shadow_ENABLED and not server_ENABLED:
    159         print("Warning: shadow requires server to be enabled!")
    160         shadow_ENABLED = False
    161     if cymaths_ENABLED and not server_ENABLED:
    162         print("Warning: cymaths requires server to be enabled!")
    163         cymaths_ENABLED = False
    164316    if x11_ENABLED and WIN32:
    165317        print("Warning: enabling x11 on MS Windows is unlikely to work!")
    166     if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED and not qt4_ENABLED:
     318    if gtk_x11_ENABLED and not x11_ENABLED:
     319        print("Error: you must enable x11 to support gtk_x11!")
     320        exit(1)
     321    if client_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
    167322        print("Warning: client is enabled but none of the client toolkits are!?")
    168     if not argb_ENABLED and (x11_ENABLED or OSX):
    169         print("Error: argb is required for x11 and osx builds!")
    170         exit(1)
    171     if not client_ENABLED and not server_ENABLED:
    172         print("Error: you must build at least the client or server!")
    173         exit(1)
    174 
     323    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     324        print("Warning: you probably want to build at least the client or server!")
     325    if DEFAULT and not pillow_ENABLED:
     326        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     327    if minify_ENABLED:
     328        r = get_status_output(["uglifyjs", "--version"])[0]
     329        if r==0:
     330            minifier = "uglifyjs"
     331        else:
     332            print("Warning: uglifyjs failed and return %i" % r)
     333            try:
     334                import yuicompressor
     335                assert yuicompressor
     336                minifier = "yuicompressor"
     337            except ImportError as e:
     338                print("Warning: yuicompressor module not found, cannot minify")
     339                minify_ENABLED = False
     340    if DEFAULT and (not enc_x264_ENABLED and not vpx_ENABLED):
     341        print("Warning: no x264 and no vpx support!")
     342        print(" you should enable at least one of these two video encodings")
     343
     344if install is None and WIN32:
     345    install = os.environ.get("MINGW_PREFIX", sys.prefix or "dist")
     346if share_xpra is None:
     347    if "install_exe" in sys.argv:
     348        #install_exe already honours the install prefix,
     349        #and the win32 bundle places share/xpra/* in the root directory:
     350        share_xpra = "."
     351    else:
     352        share_xpra = os.path.join("share", "xpra")
    175353
    176354#*******************************************************************************
    177 # build options, these may get modified further down..
    178 #
    179 setup_options = {}
    180 setup_options["name"] = "xpra"
    181 setup_options["author"] = "Antoine Martin"
    182 setup_options["author_email"] = "antoine@devloop.org.uk"
    183 setup_options["version"] = xpra.__version__
    184 setup_options["url"] = "http://xpra.org/"
    185 setup_options["download_url"] = "http://xpra.org/src/"
    186 setup_options["description"] = "Xpra: 'screen for X' utility"
    187 
    188 xpra_desc = "'screen for X' -- a tool to detach/reattach running X programs"
    189 setup_options["long_description"] = xpra_desc
    190 data_files = []
    191 setup_options["data_files"] = data_files
    192 modules = []
    193 setup_options["py_modules"] = modules
    194 packages = []       #used by py2app and py2exe
    195 excludes = []       #only used by py2exe on win32
    196 ext_modules = []
    197 cmdclass = {}
    198 scripts = []
    199 
    200 external_includes = ["cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms",
    201                      "Crypto", "Crypto.Cipher",
    202                      "hashlib",
    203                      "PIL", "PIL.Image",
     355# default sets:
     356
     357external_includes = ["hashlib",
    204358                     "ctypes", "platform"]
     359
     360
     361if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     362    external_includes += ["gi"]
     363elif gtk2_ENABLED or x11_ENABLED:
     364    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205365
    206366external_excludes = [
     
    212372                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213373                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
    218                     "distutils", "setuptools", "doctest"
     374                    "curses", "pdb",
     375                    "tty",
     376                    "setuptools", "doctest"
    219377                    ]
    220 
     378if not html5_ENABLED and not crypto_ENABLED:
     379    external_excludes += ["ssl", "_ssl"]
     380if not html5_ENABLED:
     381    external_excludes += ["BaseHTTPServer", "mimetypes"]
     382if not html5_ENABLED and not client_ENABLED:
     383    external_excludes += ["mimetools"]
     384
     385if not client_ENABLED and not server_ENABLED:
     386    excludes += ["PIL"]
     387if not dbus_ENABLED:
     388    excludes += ["dbus"]
    221389
    222390
    223391#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     392#for distutils / py2app and cx_freeze
    225393#use the following functions, which should get the right
    226394#data in the global variables "packages", "modules" and "excludes"
     
    253421
    254422def add_modules(*mods):
     423    def add(v):
     424        global modules
     425        if v not in modules:
     426            modules.append(v)
     427    do_add_modules(add, *mods)
     428
     429def do_add_modules(op, *mods):
    255430    """ adds the packages and any .py module found in the packages to the "modules" list
    256431    """
    257432    global modules
    258433    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     434        #ugly path stripping:
     435        if x.startswith("./"):
     436            x = x[2:]
     437        if x.endswith(".py"):
     438            x = x[:-3]
     439            x = x.replace("/", ".") #.replace("\\", ".")
    261440        pathname = os.path.sep.join(x.split("."))
     441        #is this a file module?
     442        f = "%s.py" % pathname
     443        if os.path.exists(f) and os.path.isfile(f):
     444            op(x)
    262445        if os.path.exists(pathname) and os.path.isdir(pathname):
    263446            #add all file modules found in this directory
    264447            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     448                #make sure we only include python files,
     449                #and ignore eclipse copies
     450                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266451                    fname = os.path.join(pathname, f)
    267452                    if os.path.isfile(fname):
    268453                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     454                        op(modname)
    270455
    271456def toggle_packages(enabled, *module_names):
     
    275460        remove_packages(*module_names)
    276461
     462def toggle_modules(enabled, *module_names):
     463    if enabled:
     464        def op(v):
     465            global modules
     466            if v not in modules:
     467                modules.append(v)
     468        do_add_modules(op, *module_names)
     469    else:
     470        remove_packages(*module_names)
     471
     472
    277473#always included:
    278 add_modules("xpra",
    279             "xpra.platform",
    280             "xpra.codecs",
    281             "xpra.codecs.xor")
    282 add_packages("xpra.scripts", "xpra.keyboard", "xpra.net")
    283 
     474if modules_ENABLED:
     475    add_modules("xpra", "xpra.platform", "xpra.net")
     476    add_modules("xpra.scripts.main")
     477
     478
     479def add_data_files(target_dir, files):
     480    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     481    assert isinstance(target_dir, str)
     482    assert isinstance(files, (list, tuple))
     483    data_files.append((target_dir, files))
     484
     485
     486#for pretty printing of options:
     487def print_option(prefix, k, v):
     488    if isinstance(v, dict):
     489        print("%s* %s:" % (prefix, k))
     490        for kk,vv in v.items():
     491            print_option(" "+prefix, kk, vv)
     492    else:
     493        print("%s* %s=%s" % (prefix, k, v))
    284494
    285495#*******************************************************************************
    286496# Utility methods for building with Cython
     497def cython_version_compare(min_version):
     498    from distutils.version import LooseVersion
     499    assert cython_ENABLED
     500    from Cython.Compiler.Version import version as cython_version
     501    return LooseVersion(cython_version) >= LooseVersion(min_version)
     502
    287503def cython_version_check(min_version):
    288     try:
     504    if not cython_version_compare(min_version):
    289505        from Cython.Compiler.Version import version as cython_version
    290     except ImportError, e:
    291         sys.exit("ERROR: Cannot find Cython: %s" % e)
    292     from distutils.version import LooseVersion
    293     if LooseVersion(cython_version) < LooseVersion(".".join([str(x) for x in min_version])):
    294506        sys.exit("ERROR: Your version of Cython is too old to build this package\n"
    295507                 "You have version %s\n"
    296508                 "Please upgrade to Cython %s or better"
    297                  % (cython_version, ".".join([str(part) for part in min_version])))
    298 
    299 def cython_add(extension, min_version=(0, 14, 0)):
     509                 % (cython_version, min_version))
     510
     511def cython_add(extension, min_version="0.20"):
    300512    #gentoo does weird things, calls --no-compile with build *and* install
    301513    #then expects to find the cython modules!? ie:
    302     #python2.7 setup.py build -b build-2.7 install --no-compile --root=/var/tmp/portage/x11-wm/xpra-0.7.0/temp/images/2.7
     514    #python2.7 setup.py build -b build-2.7 install --no-compile \
     515    #    --root=/var/tmp/portage/x11-wm/xpra-0.7.0/temp/images/2.7
    303516    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304517        return
    305     global ext_modules, cmdclass
     518    assert cython_ENABLED, "cython compilation is disabled"
    306519    cython_version_check(min_version)
    307520    from Cython.Distutils import build_ext
    308521    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     522    global cmdclass
     523    cmdclass['build_ext'] = build_ext
     524
     525def insert_into_keywords(kw, key, *args):
     526    values = kw.setdefault(key, [])
     527    for arg in args:
     528        values.insert(0, arg)
    310529
    311530def add_to_keywords(kw, key, *args):
     
    319538
    320539
     540def checkdirs(*dirs):
     541    for d in dirs:
     542        if not os.path.exists(d) or not os.path.isdir(d):
     543            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     544
    321545PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
     546#override the pkgconfig file,
     547#we don't need to link against any of these:
     548gtk2_ignored_tokens=[("-l%s" % x) for x in
     549                     ["fontconfig", "freetype", "cairo",
     550                      "atk-1.0", "pangoft2-1.0", "pango-1.0", "pangocairo-1.0",
     551                      "gio-2.0", "gdk_pixbuf-2.0"]]
    322552
    323553GCC_VERSION = []
    324554def get_gcc_version():
    325555    global GCC_VERSION
    326     if len(GCC_VERSION)==0:
    327         cmd = [os.environ.get("CC", "gcc"), "-v"]
    328         proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    329         output, _ = proc.communicate()
    330         status = proc.wait()
    331         if status==0:
     556    if not GCC_VERSION:
     557        cc = os.environ.get("CC", "gcc")
     558        r, _, err = get_status_output([cc]+["-v"])
     559        if r==0:
    332560            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     561            for line in err.splitlines():
    334562                if line.startswith(V_LINE):
    335563                    v_str = line[len(V_LINE):].split(" ")[0]
     
    337565                        try:
    338566                            GCC_VERSION.append(int(p))
    339                         except:
     567                        except ValueError:
    340568                            break
    341569                    print("found gcc version: %s" % ".".join([str(x) for x in GCC_VERSION]))
     
    343571    return GCC_VERSION
    344572
    345 def make_constants_pxi(constants_path, pxi_path):
     573def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346574    constants = []
    347     for line in open(constants_path):
    348         data = line.split("#", 1)[0].strip()
    349         # data can be empty ''...
    350         if not data:
    351             continue
    352         # or a pair like 'cFoo "Foo"'...
    353         elif len(data.split()) == 2:
    354             (pyname, cname) = data.split()
    355             constants.append((pyname, cname))
    356         # or just a simple token 'Foo'
    357         else:
    358             constants.append(data)
    359     out = open(pxi_path, "w")
    360     out.write("cdef extern from *:\n")
    361     ### Apparently you can't use | on enum's?!
    362     # out.write("    enum MagicNumbers:\n")
    363     # for const in constants:
    364     #     if isinstance(const, tuple):
    365     #         out.write('        %s %s\n' % const)
    366     #     else:
    367     #         out.write('        %s\n' % (const,))
    368     for const in constants:
    369         if isinstance(const, tuple):
    370             out.write('    unsigned int %s %s\n' % const)
    371         else:
    372             out.write('    unsigned int %s\n' % (const,))
    373 
    374     out.write("constants = {\n")
    375     for const in constants:
    376         if isinstance(const, tuple):
    377             pyname = const[0]
    378         else:
    379             pyname = const
    380         out.write('    "%s": %s,\n' % (pyname, pyname))
    381     out.write("}\n")
    382 
    383 def make_constants(*paths):
     575    with open(constants_path) as f:
     576        for line in f:
     577            data = line.split("#", 1)[0].strip()
     578            # data can be empty ''...
     579            if not data:
     580                continue
     581            # or a pair like 'cFoo "Foo"'...
     582            elif len(data.split()) == 2:
     583                (pyname, cname) = data.split()
     584                constants.append((pyname, cname))
     585            # or just a simple token 'Foo'
     586            else:
     587                constants.append(data)
     588
     589    with open(pxi_path, "w") as out:
     590        if constants:
     591            out.write("cdef extern from *:\n")
     592            ### Apparently you can't use | on enum's?!
     593            # out.write("    enum MagicNumbers:\n")
     594            # for const in constants:
     595            #     if isinstance(const, tuple):
     596            #         out.write('        %s %s\n' % const)
     597            #     else:
     598            #         out.write('        %s\n' % (const,))
     599            for const in constants:
     600                if isinstance(const, tuple):
     601                    out.write('    unsigned int %s %s\n' % const)
     602                else:
     603                    out.write('    unsigned int %s\n' % (const,))
     604
     605            out.write("constants = {\n")
     606            for const in constants:
     607                if isinstance(const, tuple):
     608                    pyname = const[0]
     609                else:
     610                    pyname = const
     611                out.write('    "%s": %s,\n' % (pyname, pyname))
     612            out.write("}\n")
     613            if kwargs:
     614                out.write("\n\n")
     615
     616        if kwargs:
     617            for k, v in kwargs.items():
     618                out.write('DEF %s = %s\n' % (k, v))
     619
     620
     621def should_rebuild(src_file, bin_file):
     622    if not os.path.exists(bin_file):
     623        return "no file"
     624    if rebuild_ENABLED:
     625        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     626            return "binary file out of date"
     627        if os.path.getctime(bin_file)<os.path.getctime(__file__):
     628            return "newer build file"
     629    return None
     630
     631def make_constants(*paths, **kwargs):
    384632    base = os.path.join(os.getcwd(), *paths)
    385633    constants_file = "%s.txt" % base
    386     pxi_file = "%s.pxi" % base
    387     reason = None
    388     if not os.path.exists(pxi_file):
    389         reason = "no pxi file"
    390     elif os.path.getctime(pxi_file)<os.path.getctime(constants_file):
    391         reason = "pxi file out of date"
    392     elif os.path.getctime(pxi_file)<os.path.getctime(__file__):
    393         reason = "newer build file"
     634    try:
     635        pxi_file = kwargs.pop("pxi_file")
     636    except KeyError:
     637        pxi_file = "%s.pxi" % base
     638    reason = should_rebuild(constants_file, pxi_file)
    394639    if reason:
    395640        if verbose_ENABLED:
    396641            print("(re)generating %s (%s):" % (pxi_file, reason))
    397         make_constants_pxi(constants_file, pxi_file)
    398 
    399 
    400 def static_link_args(*libnames):
    401     return ["-Wl,-Bstatic"] + ["-l%s" % x for x in libnames] + ["-Wl,-Bsymbolic", "-Wl,-Bdynamic"]
    402 
    403 def get_static_pkgconfig(*libnames):
    404     defs = pkgconfig()
    405     remove_from_keywords(defs, 'extra_compile_args', '-fsanitize=address')
    406     if os.name=="posix":
    407         if debug_ENABLED:
    408             add_to_keywords(defs, 'extra_link_args', "-Wl,--verbose")
    409         defs.update({'include_dirs': ["/usr/local/include"],
    410                      'library_dirs': ["/usr/local/lib", "/usr/local/lib64"]})
    411     if len(libnames)>0:
    412         add_to_keywords(defs,  'extra_link_args', *static_link_args(*libnames))
    413     return defs
     642        make_constants_pxi(constants_file, pxi_file, **kwargs)
     643
    414644
    415645# Tweaked from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/502261
    416 def pkgconfig(*pkgs_options, **ekw):
    417     static = ekw.get("static", None)
    418     if static is not None:
    419         del ekw["static"]
    420         if static:
    421             return get_static_pkgconfig(*pkgs_options)
    422 
     646def exec_pkgconfig(*pkgs_options, **ekw):
    423647    kw = dict(ekw)
    424     if len(pkgs_options)>0:
     648    optimize = kw.pop("optimize", None)
     649    if optimize and not debug_ENABLED:
     650        if isinstance(optimize, bool):
     651            optimize = int(optimize)*3
     652        add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     653    ignored_flags = kw.pop("ignored_flags", [])
     654    ignored_tokens = kw.pop("ignored_tokens", [])
     655
     656    #for distros that don't patch distutils,
     657    #we have to add the python cflags:
     658    if not (is_Fedora() or is_Debian() or is_CentOS()):
     659        import shlex
     660        import sysconfig
     661        for cflag in shlex.split(sysconfig.get_config_var('CFLAGS') or ''):
     662            add_to_keywords(kw, 'extra_compile_args', cflag)
     663
     664    def add_tokens(s, extra="extra_link_args", extra_map={"-W" : "extra_compile_args"}):
     665        if not s:
     666            return
     667        flag_map = {'-I': 'include_dirs',
     668                    '-L': 'library_dirs',
     669                    '-l': 'libraries'}
     670        for token in s.split():
     671            if token in ignored_tokens:
     672                pass
     673            elif token[:2] in ignored_flags:
     674                pass
     675            elif token[:2] in flag_map:
     676                if len(token)>2:
     677                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     678                else:
     679                    print("Warning: invalid token '%s'" % token)
     680            else:
     681                extra_name = extra_map.get(token, extra)
     682                add_to_keywords(kw, extra_name, token)
     683
     684    if pkgs_options:
    425685        package_names = []
    426686        #find out which package name to use from potentially many options
     
    429689            #for this package options, find the ones that work
    430690            valid_option = None
    431             if type(package_options)==str:
     691            if isinstance(package_options, str):
    432692                options = [package_options]     #got given just one string
    433                 if not package_options.startswith("lib"):
    434                     options.append("lib%s" % package_options)
    435693            else:
    436                 assert type(package_options)==list
     694                assert isinstance(package_options, list)
    437695                options = package_options       #got given a list of options
    438696            for option in options:
    439697                cmd = ["pkg-config", "--exists", option]
    440                 proc = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    441                 status = proc.wait()
    442                 if status==0:
     698                r = get_status_output(cmd)[0]
     699                if r==0:
    443700                    valid_option = option
    444701                    break
    445702            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     703                raise Exception("ERROR: cannot find a valid pkg-config entry for %s using PKG_CONFIG_PATH=%s" %
     704                                (" or ".join(options), os.environ.get("PKG_CONFIG_PATH", "(empty)")))
    447705            package_names.append(valid_option)
    448706        if verbose_ENABLED and list(pkgs_options)!=list(package_names):
    449             print("pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
    450         flag_map = {'-I': 'include_dirs',
    451                     '-L': 'library_dirs',
    452                     '-l': 'libraries'}
    453         cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
    454         proc = subprocess.Popen(cmd, env=os.environ, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    455         (output, _) = proc.communicate()
    456         status = proc.wait()
    457         if status!=0:
    458             sys.exit("ERROR: call to pkg-config ('%s') failed" % " ".join(cmd))
    459         if sys.version>='3':
    460             output = output.decode('utf-8')
    461         for token in output.split():
    462             if token[:2] in flag_map:
    463                 add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
    464             else: # throw others to extra_link_args
    465                 add_to_keywords(kw, 'extra_link_args', token)
    466             for k, v in kw.items(): # remove duplicates
    467                 kw[k] = list(set(v))
     707            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     708        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     709        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     710        if r!=0:
     711            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     712        add_tokens(pkg_config_out)
    468713    if warn_ENABLED:
    469714        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470715        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471716    if strict_ENABLED:
    472         #these are almost certainly real errors since our code is "clean":
    473         if get_gcc_version()>=[4, 4]:
    474             eifd = "-Werror=implicit-function-declaration"
     717        if os.environ.get("CC", "").find("clang")>=0:
     718            #clang emits too many warnings with cython code,
     719            #so we can't enable Werror without turning off some warnings:
     720            #this list of flags should allow clang to build the whole source tree,
     721            #as of Cython 0.26 + clang 4.0. Other version combinations may require
     722            #(un)commenting other switches.
     723            eifd = ["-Werror",
     724                    #"-Wno-unneeded-internal-declaration",
     725                    #"-Wno-unknown-attributes",
     726                    #"-Wno-unused-function",
     727                    #"-Wno-self-assign",
     728                    #"-Wno-sometimes-uninitialized",
     729                    #cython adds rpath to the compilation command??
     730                    #and the "-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1" is also ignored by clang:
     731                    "-Wno-deprecated-register",
     732                    "-Wno-unused-command-line-argument",
     733                    ]
     734        elif get_gcc_version()>=[4, 4]:
     735            eifd = ["-Werror"]
     736            if is_Debian() or is_Ubuntu() or is_Raspbian():
     737                #needed on Debian and Ubuntu to avoid this error:
     738                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1:
     739                # error: function declaration isn't a prototype [-Werror=strict-prototypes]
     740                eifd.append("-Wno-error=strict-prototypes")
     741                #the cython version shipped with Xenial emits warnings:
     742                if (14,4)<getUbuntuVersion()<=(16,4):
     743                    eifd.append("-Wno-error=shift-count-overflow")
     744                    eifd.append("-Wno-error=sign-compare")
     745            if NETBSD:
     746                #see: http://trac.cython.org/ticket/395
     747                eifd += ["-fno-strict-aliasing"]
     748            elif FREEBSD:
     749                eifd += ["-Wno-error=unused-function"]
    475750        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     751            #older versions of OSX ship an old gcc,
     752            #not much we can do with this:
     753            eifd = []
     754        for eif in eifd:
     755            add_to_keywords(kw, 'extra_compile_args', eif)
     756    if sys.version_info>=(3,7):
     757        #we'll switch to the "new" buffer interface after we drop support for Python 2.7
     758        #until then, silence those deprecation warnings:
     759        add_to_keywords(kw, 'extra_compile_args', "-Wno-error=deprecated-declarations")
    478760    if PIC_ENABLED:
    479761        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481763        add_to_keywords(kw, 'extra_compile_args', '-g')
    482764        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     765        if get_gcc_version()>=[4, 8]:
    485766            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486767            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     768    if rpath and kw.get("libraries"):
     769        insert_into_keywords(kw, "library_dirs", rpath)
     770        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
     771    add_tokens(os.environ.get("CFLAGS"), "extra_compile_args", {})
     772    add_tokens(os.environ.get("LDFLAGS"), "extra_link_args", {})
    487773    #add_to_keywords(kw, 'include_dirs', '.')
    488774    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     775        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490776    return kw
     777pkgconfig = exec_pkgconfig
    491778
    492779
    493780#*******************************************************************************
    494 def get_xorg_conf_and_script():
    495     if not server_ENABLED:
    496         return "etc/xpra/client-only/xpra.conf", False
    497 
    498     def Xvfb():
    499         return "etc/xpra/Xvfb/xpra.conf", False
    500 
    501     if sys.platform.find("bsd")>=0:
    502         print("Warning: sorry, no support for Xdummy on %s" % sys.platform)
    503         return Xvfb()
    504 
    505     XORG_BIN = None
    506     PATHS = os.environ.get("PATH").split(os.pathsep)
    507     for x in PATHS:
    508         xorg = os.path.join(x, "Xorg")
    509         if os.path.isfile(xorg):
    510             XORG_BIN = xorg
    511             break
    512     if not XORG_BIN:
    513         print("Xorg not found, cannot detect version or Xdummy support")
    514         return Xvfb()
    515 
    516     def Xorg_suid_check():
    517         xorg_stat = os.stat(XORG_BIN)
    518         if (xorg_stat.st_mode & stat.S_ISUID)!=0:
    519             if (xorg_stat.st_mode & stat.S_IROTH)==0:
    520                 print("Xorg is suid and not readable, Xdummy support unavailable")
    521                 return Xvfb()
    522             print("%s is suid and readable, using the xpra_Xdummy wrapper" % XORG_BIN)
    523             return "etc/xpra/xpra_Xdummy/xpra.conf", True
    524         else:
    525             print("using Xdummy config file")
    526             return "etc/xpra/Xdummy/xpra.conf", False
    527 
    528     if Xdummy_ENABLED is False:
    529         return Xvfb()
    530     elif Xdummy_ENABLED is True:
    531         print("Xdummy support specified as 'enabled', will detect suid mode")
    532         return Xorg_suid_check()
     781
     782
     783def get_base_conf_dir(install_dir, stripbuildroot=True):
     784    #in some cases we want to strip the buildroot (to generate paths in the config file)
     785    #but in other cases we want the buildroot path (when writing out the config files)
     786    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     787    #this is a bit hackish, but I can't think of a better way of detecting it
     788    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     789    dirs = (install_dir or sys.prefix).split(os.path.sep)
     790    if install_dir and stripbuildroot:
     791        pkgdir = os.environ.get("pkgdir")
     792        if "debian" in dirs and "tmp" in dirs:
     793            #ugly fix for stripping the debian tmp dir:
     794            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     795            while "tmp" in dirs:
     796                dirs = dirs[dirs.index("tmp")+1:]
     797        elif "debian" in dirs:
     798            #same for recent debian versions:
     799            #ie: "xpra-2.0.2/debian/xpra/usr" -> "usr"
     800            i = dirs.index("debian")
     801            if dirs[i+1] == "xpra":
     802                dirs = dirs[i+2:]
     803        elif "BUILDROOT" in dirs:
     804            #strip rpm style build root:
     805            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     806            dirs = dirs[dirs.index("BUILDROOT")+2:]
     807        elif pkgdir and install_dir.startswith(pkgdir):
     808            #arch build dir:
     809            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     810        elif "usr" in dirs:
     811            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     812            #assume "/usr" or "/usr/local" is the build root
     813            while "usr" in dirs[1:]:
     814                dirs = dirs[dirs[1:].index("usr")+1:]
     815        elif "image" in dirs:
     816            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     817            while "image" in dirs:
     818                dirs = dirs[dirs.index("image")+2:]
     819    #now deal with the fact that "/etc" is used for the "/usr" prefix
     820    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     821    if dirs and dirs[-1]=="usr":
     822        dirs = dirs[:-1]
     823    #is this an absolute path?
     824    if not dirs or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     825        #ie: ["/", "usr"] or ["/", "usr", "local"]
     826        dirs.insert(0, os.path.sep)
     827    return dirs
     828
     829def get_conf_dir(install_dir, stripbuildroot=True):
     830    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     831    dirs.append("etc")
     832    dirs.append("xpra")
     833    return os.path.join(*dirs)
     834
     835def detect_xorg_setup(install_dir=None):
     836    from xpra.scripts import config
     837    config.debug = config.warn
     838    conf_dir = get_conf_dir(install_dir)
     839    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     840
     841def build_xpra_conf(install_dir):
     842    #generates an actual config file from the template
     843    xvfb_command = detect_xorg_setup(install_dir)
     844    from xpra.platform.features import DEFAULT_ENV
     845    def bstr(b):
     846        if b is None:
     847            return "auto"
     848        return "yes" if int(b) else "no"
     849    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     850    conf_dir = get_conf_dir(install_dir)
     851    from xpra.platform.features import DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     852    from xpra.platform.paths import get_socket_dirs
     853    from xpra.scripts.config import (
     854        get_default_key_shortcuts, get_default_systemd_run, get_default_pulseaudio_command,
     855        DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO,
     856        )
     857    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     858    socket_dirs = get_socket_dirs()
     859    if WIN32:
     860        bind = "Main"
    533861    else:
    534         print("Xdummy support unspecified, will try to detect")
    535 
    536     cmd = ["lsb_release", "-cs"]
    537     try:
    538         proc = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    539         out, _ = proc.communicate()
    540         release = out.replace("\n", "")
    541         print("Found OS release: %s" % release)
    542         if release in ("raring", "saucy"):
    543             #yet another instance of Ubuntu breaking something
    544             print("Warning: Ubuntu '%s' breaks Xorg/Xdummy usage - using Xvfb fallback" % release)
    545             return  Xvfb()
    546     except Exception, e:
    547         print("failed to detect OS release using %s: %s" % (" ".join(cmd), e))
    548 
    549     #do live detection
    550     cmd = ["Xorg", "-version"]
    551     if verbose_ENABLED:
    552         print("detecting Xorg version using: %s" % str(cmd))
    553     try:
    554         proc = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    555         out, _ = proc.communicate()
    556         V_LINE = "X.Org X Server "
    557         xorg_version = None
    558         for line in out.decode("utf8").splitlines():
    559             if line.startswith(V_LINE):
    560                 v_str = line[len(V_LINE):]
    561                 xorg_version = [int(x) for x in v_str.split(".")[:2]]
    562                 break
    563         if not xorg_version:
    564             print("Xorg version could not be detected, Xdummy support unavailable")
    565             return Xvfb()
    566         if xorg_version<[1, 12]:
    567             print("Xorg version %s is too old (1.12 or later required), Xdummy support not available" % v_str)
    568             return Xvfb()
    569         print("found valid recent version of Xorg server: %s" % v_str)
    570         return Xorg_suid_check()
    571     except Exception, e:
    572         print("failed to detect Xorg version: %s" % e)
    573         print("not installing Xdummy support")
    574         traceback.print_exc()
    575         return  Xvfb()
     862        if os.getuid()>0:
     863            #remove any paths containing the uid,
     864            #osx uses /var/tmp/$UID-Xpra,
     865            #but this should not be included in the default config for all users!
     866            #(the buildbot's uid!)
     867            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     868        bind = "auto"
     869    #FIXME: we should probably get these values from the default config instead
     870    pdf, postscript = "", ""
     871    if POSIX and printing_ENABLED:
     872        try:
     873            if "/usr/sbin" not in sys.path:
     874                sys.path.append("/usr/sbin")
     875            from xpra.platform.pycups_printing import get_printer_definition
     876            print("probing cups printer definitions")
     877            pdf = get_printer_definition("pdf")
     878            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     879            print("pdf=%s, postscript=%s" % (pdf, postscript))
     880        except Exception as e:
     881            print("could not probe for pdf/postscript printers: %s" % e)
     882    def pretty_cmd(cmd):
     883        return " ".join(cmd)
     884    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     885    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     886    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==(16, 10))
     887    #no python-avahi on RH / CentOS, need dbus module on *nix:
     888    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     889    SUBS = {
     890            'xvfb_command'          : pretty_cmd(xvfb_command),
     891            'ssh_command'           : "auto",
     892            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     893            'remote_logging'        : "both",
     894            'start_env'             : start_env,
     895            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     896            'pulseaudio_command'    : pretty_cmd(get_default_pulseaudio_command()),
     897            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     898            'conf_dir'              : conf_dir,
     899            'bind'                  : bind,
     900            'ssl_cert'              : ssl_cert or "",
     901            'ssl_key'               : ssl_key or "",
     902            'systemd_run'           : get_default_systemd_run(),
     903            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     904            'log_dir'               : "auto",
     905            'mdns'                  : bstr(mdns),
     906            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     907            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     908            'pdf_printer'           : pdf,
     909            'postscript_printer'    : postscript,
     910            'webcam'                : ["no", "auto"][webcam],
     911            'mousewheel'            : "on",
     912            'printing'              : bstr(printing_ENABLED),
     913            'dbus_control'          : bstr(dbus_ENABLED),
     914            'mmap'                  : bstr(True),
     915            }
     916    def convert_templates(subdirs):
     917        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     918        #get conf dir for install, without stripping the build root
     919        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     920        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     921        if not os.path.exists(target_dir):
     922            try:
     923                os.makedirs(target_dir)
     924            except Exception as e:
     925                print("cannot create target dir '%s': %s" % (target_dir, e))
     926        for f in sorted(os.listdir(dirname)):
     927            if f.endswith("osx.conf.in") and not OSX:
     928                continue
     929            filename = os.path.join(dirname, f)
     930            if os.path.isdir(filename):
     931                convert_templates(subdirs+[f])
     932                continue
     933            if not f.endswith(".in"):
     934                continue
     935            with open(filename, "r") as f_in:
     936                template  = f_in.read()
     937            target_file = os.path.join(target_dir, f[:-len(".in")])
     938            print("generating %s from %s" % (target_file, f))
     939            with open(target_file, "w") as f_out:
     940                config_data = template % SUBS
     941                f_out.write(config_data)
     942    convert_templates([])
    576943
    577944
    578945#*******************************************************************************
    579 if 'clean' in sys.argv or 'sdist' in sys.argv:
     946def clean():
    580947    #clean and sdist don't actually use cython,
    581948    #so skip this (and avoid errors)
    582     def pkgconfig(*pkgs_options, **ekw):
    583         return {}
     949    global pkgconfig
     950    pkgconfig = no_pkgconfig
    584951    #always include everything in this case:
    585952    add_packages("xpra")
    586953    #ensure we remove the files we generate:
    587954    CLEAN_FILES = [
    588                    "xpra/gtk_common/gdk_atoms.c",
    589                    "xpra/x11/gtk_x11/constants.pxi",
    590                    "xpra/x11/gtk_x11/gdk_bindings.c",
    591                    "xpra/x11/gtk_x11/gdk_display_source.c",
     955                   "xpra/build_info.py",
     956                   "xpra/monotonic_time.c",
     957                   "xpra/gtk_common/gtk2/gdk_atoms.c",
     958                   "xpra/gtk_common/gtk2/gdk_bindings.c",
     959                   "xpra/gtk_common/gtk3/gdk_atoms.c",
     960                   "xpra/gtk_common/gtk3/gdk_bindings.c",
     961                   "xpra/x11/gtk2/constants.pxi",
     962                   "xpra/x11/gtk2/gdk_bindings.c",
     963                   "xpra/x11/gtk2/gdk_display_source.c",
     964                   "xpra/x11/gtk3/constants.pxi",
     965                   "xpra/x11/gtk3/gdk_bindings.c",
     966                   "xpra/x11/gtk3/gdk_display_source.c",
    592967                   "xpra/x11/bindings/constants.pxi",
    593968                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597972                   "xpra/x11/bindings/randr_bindings.c",
    598973                   "xpra/x11/bindings/core_bindings.c",
     974                   "xpra/x11/bindings/posix_display_source.c",
    599975                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     976                   "xpra/x11/bindings/xi2_bindings.c",
     977                   "xpra/platform/win32/propsys.cpp",
     978                   "xpra/platform/darwin/gdk_bindings.c",
     979                   "xpra/platform/xposix/sd_listen.c",
     980                   "xpra/platform/xposix/netdev_query.c",
     981                   "xpra/net/bencode/cython_bencode.c",
     982                   "xpra/net/vsock.c",
     983                   "xpra/buffers/membuf.c",
    601984                   "xpra/codecs/vpx/encoder.c",
    602985                   "xpra/codecs/vpx/decoder.c",
    603986                   "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     987                   "xpra/codecs/nvfbc/fbc_capture_linux.cpp",
     988                   "xpra/codecs/nvfbc/fbc_capture_win.cpp",
    605989                   "xpra/codecs/enc_x264/encoder.c",
    606990                   "xpra/codecs/enc_x265/encoder.c",
     991                   "xpra/codecs/jpeg/encoder.c",
     992                   "xpra/codecs/jpeg/decoder.c",
     993                   "xpra/codecs/enc_ffmpeg/encoder.c",
     994                   "xpra/codecs/v4l2/constants.pxi",
     995                   "xpra/codecs/v4l2/pusher.c",
     996                   "xpra/codecs/libav_common/av_log.c",
    607997                   "xpra/codecs/webp/encode.c",
    608                    "xpra/codecs/dec_avcodec/decoder.c",
    609                    "xpra/codecs/dec_avcodec/constants.pxi",
     998                   "xpra/codecs/webp/decode.c",
    610999                   "xpra/codecs/dec_avcodec2/decoder.c",
     1000                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    6111001                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    6141002                   "xpra/codecs/xor/cyxor.c",
    6151003                   "xpra/codecs/argb/argb.c",
    616                    "xpra/server/stats/cymaths.c",
    617                    "etc/xpra/xpra.conf"]
    618     if sys.platform.startswith("win"):
    619         #on win32, the build creates ".pyd" files, clean those too:
    620         for x in list(CLEAN_FILES):
    621             if x.endswith(".c"):
    622                 CLEAN_FILES.append(x[:-2]+".pyd")
     1004                   "xpra/codecs/nvapi_version.c",
     1005                   "xpra/gtk_common/gdk_atoms.c",
     1006                   "xpra/client/gtk3/cairo_workaround.c",
     1007                   "xpra/server/cystats.c",
     1008                   "xpra/rectangle.c",
     1009                   "xpra/server/window/motion.c",
     1010                   "xpra/server/pam.c",
     1011                   "etc/xpra/xpra.conf",
     1012                   #special case for the generated xpra conf files in build (see #891):
     1013                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     1014    if cuda_rebuild_ENABLED:
     1015        CLEAN_FILES += [
     1016            "xpra/codecs/cuda_common/ARGB_to_NV12.fatbin",
     1017            "xpra/codecs/cuda_common/ARGB_to_YUV444.fatbin",
     1018            "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     1019            "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
     1020            ]
     1021    for x in CLEAN_FILES:
     1022        p, ext = os.path.splitext(x)
     1023        if ext in (".c", ".cpp", ".pxi"):
     1024            #clean the Cython annotated html files:
     1025            CLEAN_FILES.append(p+".html")
     1026            if WIN32 and ext!=".pxi":
     1027                #on win32, the build creates ".pyd" files, clean those too:
     1028                CLEAN_FILES.append(p+".pyd")
     1029                #when building with python3, we need to clean files named like:
     1030                #"xpra/codecs/csc_libyuv/colorspace_converter-cpython-36m.dll"
     1031                filename = os.path.join(os.getcwd(), p.replace("/", os.path.sep)+"*.dll")
     1032                CLEAN_FILES += glob.glob(filename)
    6231033    if 'clean' in sys.argv:
    6241034        CLEAN_FILES.append("xpra/build_info.py")
     
    6301040            os.unlink(filename)
    6311041
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     1042if 'clean' in sys.argv or 'sdist' in sys.argv:
     1043    clean()
     1044
     1045from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    6331046
    6341047if "clean" not in sys.argv:
    6351048    # Add build info to build_info.py file:
    6361049    record_build_info()
     1050    if modules_ENABLED:
     1051        # ensure it is included in the module list if it didn't exist before
     1052        add_modules(BUILD_INFO_FILE)
    6371053
    6381054if "sdist" in sys.argv:
    6391055    record_src_info()
    6401056
    641 if "install" in sys.argv:
     1057if "install" in sys.argv or "build" in sys.argv:
    6421058    #if installing from source tree rather than
    6431059    #from a source snapshot, we may not have a "src_info" file
    6441060    #so create one:
    645     if not has_src_info():
     1061    if not has_src_info() and modules_ENABLED:
    6461062        record_src_info()
     1063        # ensure it is now included in the module list
     1064        add_modules(SRC_INFO_FILE)
    6471065
    6481066
     
    6591077        for f in files:
    6601078            dirname = root[len(srcdir)+1:]
    661             filename = os.path.join(root, f)
    662             m.setdefault(dirname, []).append(filename)
     1079            m.setdefault(dirname, []).append(os.path.join(root, f))
    6631080    return m
     1081
     1082
     1083def install_html5(install_dir="www"):
     1084    from setup_html5 import install_html5 as do_install_html5
     1085    do_install_html5(install_dir, minifier, html5_gzip_ENABLED, html5_brotli_ENABLED, verbose_ENABLED)
     1086
    6641087
    6651088#*******************************************************************************
    6661089if WIN32:
    667     # The Microsoft C library DLLs:
    668     # Unfortunately, these files cannot be re-distributed legally :(
    669     # So here is the md5sum so you can find the right version:
    670     # (you can find them in various packages, including Visual Studio 2008,
    671     # pywin32, etc...)
    672     import md5
    673     md5sums = {"Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest" : "37f44d535dcc8bf7a826dfa4f5fa319b",
    674                "Microsoft.VC90.CRT/msvcm90.dll"                 : "4a8bc195abdc93f0db5dab7f5093c52f",
    675                "Microsoft.VC90.CRT/msvcp90.dll"                 : "6de5c66e434a9c1729575763d891c6c2",
    676                "Microsoft.VC90.CRT/msvcr90.dll"                 : "e7d91d008fe76423962b91c43c88e4eb",
    677                "Microsoft.VC90.CRT/vcomp90.dll"                 : "f6a85f3b0e30c96c993c69da6da6079e",
    678                "Microsoft.VC90.MFC/Microsoft.VC90.MFC.manifest" : "17683bda76942b55361049b226324be9",
    679                "Microsoft.VC90.MFC/mfc90.dll"                   : "462ddcc5eb88f34aed991416f8e354b2",
    680                "Microsoft.VC90.MFC/mfc90u.dll"                  : "b9030d821e099c79de1c9125b790e2da",
    681                "Microsoft.VC90.MFC/mfcm90.dll"                  : "d4e7c1546cf3131b7d84b39f8da9e321",
    682                "Microsoft.VC90.MFC/mfcm90u.dll"                 : "371226b8346f29011137c7aa9e93f2f6",
    683                }
    684     # This is where I keep them, you will obviously need to change this value:
    685     C_DLLs = "C:\\"
    686     for dll_file, md5sum in md5sums.items():
    687         filename = os.path.join(C_DLLs, *dll_file.split("/"))
    688         if not os.path.exists(filename) or not os.path.isfile(filename):
    689             sys.exit("ERROR: DLL file %s is missing or not a file!" % filename)
    690         sys.stdout.write("* verifying md5sum for %s: " % filename)
    691         f = open(filename, mode='rb')
    692         data = f.read()
    693         f.close()
    694         m = md5.new()
    695         m.update(data)
    696         digest = m.hexdigest()
    697         assert digest==md5sum, "md5 digest for file %s does not match, expected %s but found %s" % (dll_file, md5sum, digest)
    698         sys.stdout.write("OK\n")
    699         sys.stdout.flush()
    700     #this should all be done with pkgconfig...
    701     #but until someone figures this out, the ugly path code below works
    702     #as long as you install in the same place or tweak the paths.
    703 
    704     #first some header crap so codecs can find the inttypes.h
    705     #and stdint.h:
    706     win32_include_dir = os.path.join(os.getcwd(), "win32")
    707 
    708     #cuda:
    709     cuda_path = "C:\\NVIDIA\CUDA\CUDAToolkit"
    710     cuda_include_dir   = os.path.join(cuda_path, "include")
    711     cuda_bin_dir       = os.path.join(cuda_path, "bin")
    712 
    713     #ffmpeg is needed for both swscale and x264:
    714     libffmpeg_path = None
    715     if dec_avcodec_ENABLED:
    716         assert not dec_avcodec2_ENABLED, "cannot enable both dec_avcodec and dec_avcodec2"
    717         libffmpeg_path = "C:\\ffmpeg-win32-bin"
    718     elif dec_avcodec2_ENABLED:
    719         assert not dec_avcodec_ENABLED, "cannot enable both dec_avcodec and dec_avcodec2"
    720         libffmpeg_path = "C:\\ffmpeg2-win32-bin"
    721     else:
    722         if csc_swscale_ENABLED:
    723             for p in ("C:\\ffmpeg2-win32-bin", "C:\\ffmpeg-win32-bin"):
    724                 if os.path.exists(p):
    725                     libffmpeg_path = p
    726             assert libffmpeg_path is not None, "no ffmpeg found, cannot use csc_swscale"
    727     libffmpeg_include_dir   = os.path.join(libffmpeg_path, "include")
    728     libffmpeg_lib_dir       = os.path.join(libffmpeg_path, "lib")
    729     libffmpeg_bin_dir       = os.path.join(libffmpeg_path, "bin")
    730     #x265
    731     x265_path ="C:\\x265"
    732     x265_include_dir    = x265_path
    733     x265_lib_dir        = x265_path
    734     x265_bin_dir        = x265_path
    735     #x264 (direct from build dir.. yuk - sorry!):
    736     x264_path ="C:\\x264"
    737     x264_include_dir    = x264_path
    738     x264_lib_dir        = x264_path
    739     x264_bin_dir        = x264_path
    740     # Same for vpx:
    741     # http://code.google.com/p/webm/downloads/list
    742     #the path after installing may look like this:
    743     #vpx_PATH="C:\\vpx-vp8-debug-src-x86-win32mt-vs9-v1.1.0"
    744     #but we use something more generic, without the version numbers:
    745     vpx_path = ""
    746     for p in ("C:\\vpx-1.3", "C:\\vpx-1.2", "C:\\vpx-1.1", "C:\\vpx-vp8"):
    747         if os.path.exists(p) and os.path.isdir(p):
    748             vpx_path = p
    749             break
    750     vpx_include_dir     = os.path.join(vpx_path, "include")
    751     vpx_lib_dir         = os.path.join(vpx_path, "lib", "Win32")
    752     if os.path.exists(os.path.join(vpx_lib_dir, "vpx.lib")):
    753         vpx_lib_names = ["vpx"]               #for libvpx 1.3.0
    754     elif os.path.exists(os.path.join(vpx_lib_dir, "vpxmd.lib")):
    755         vpx_lib_names = ["vpxmd"]             #for libvpx 1.2.0
    756     else:
    757         vpx_lib_names = ["vpxmt", "vpxmtd"]   #for libvpx 1.1.0
    758     #webp:
    759     webp_path = "C:\\libwebp-windows-x86"
    760     webp_include_dir    = webp_path+"\\include"
    761     webp_lib_dir        = webp_path+"\\lib"
    762     webp_bin_dir        = webp_path+"\\bin"
    763     webp_lib_names      = ["libwebp"]
    764 
    765     # Same for PyGTK:
    766     # http://www.pygtk.org/downloads.html
    767     gtk2_path = "C:\\Python27\\Lib\\site-packages\\gtk-2.0"
    768     python_include_path = "C:\\Python27\\include"
    769     gtk2runtime_path        = os.path.join(gtk2_path, "runtime")
    770     gtk2_lib_dir            = os.path.join(gtk2runtime_path, "bin")
    771     gtk2_base_include_dir   = os.path.join(gtk2runtime_path, "include")
    772 
    773     pygtk_include_dir       = os.path.join(python_include_path, "pygtk-2.0")
    774     atk_include_dir         = os.path.join(gtk2_base_include_dir, "atk-1.0")
    775     gtk2_include_dir        = os.path.join(gtk2_base_include_dir, "gtk-2.0")
    776     gdkpixbuf_include_dir   = os.path.join(gtk2_base_include_dir, "gdk-pixbuf-2.0")
    777     glib_include_dir        = os.path.join(gtk2_base_include_dir, "glib-2.0")
    778     cairo_include_dir       = os.path.join(gtk2_base_include_dir, "cairo")
    779     pango_include_dir       = os.path.join(gtk2_base_include_dir, "pango-1.0")
    780     gdkconfig_include_dir   = os.path.join(gtk2runtime_path, "lib", "gtk-2.0", "include")
    781     glibconfig_include_dir  = os.path.join(gtk2runtime_path, "lib", "glib-2.0", "include")
    782 
    783     def checkdirs(*dirs):
    784         for d in dirs:
    785             if not os.path.exists(d) or not os.path.isdir(d):
    786                 raise Exception("cannot find a directory which is required for building: %s" % d)
    787 
    788     def pkgconfig(*pkgs_options, **ekw):
    789         kw = dict(ekw)
    790         #remove static flag on win32..
    791         static = kw.get("static", None)
    792         if static is not None:
    793             del kw["static"]
    794         #always add the win32 include dirs, everyone needs that:
    795         add_to_keywords(kw, 'include_dirs', win32_include_dir)
    796         if len(pkgs_options)==0:
    797             return kw
    798 
    799         def add_to_PATH(bindir):
    800             if os.environ['PATH'].find(bindir)<0:
    801                 os.environ['PATH'] = bindir + ';' + os.environ['PATH']
    802             if bindir not in sys.path:
    803                 sys.path.append(bindir)
    804         if "avcodec" in pkgs_options[0]:
    805             add_to_PATH(libffmpeg_bin_dir)
    806             add_to_keywords(kw, 'include_dirs', libffmpeg_include_dir)
    807             add_to_keywords(kw, 'libraries', "avcodec", "avutil")
    808             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_lib_dir)
    809             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_bin_dir)
    810             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    811             checkdirs(libffmpeg_include_dir, libffmpeg_lib_dir, libffmpeg_bin_dir)
    812         elif "swscale" in pkgs_options[0]:
    813             add_to_PATH(libffmpeg_bin_dir)
    814             add_to_keywords(kw, 'include_dirs', libffmpeg_include_dir)
    815             add_to_keywords(kw, 'libraries', "swscale", "avutil")
    816             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_lib_dir)
    817             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_bin_dir)
    818             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    819             checkdirs(libffmpeg_include_dir, libffmpeg_lib_dir, libffmpeg_bin_dir)
    820         elif "x264" in pkgs_options[0]:
    821             add_to_PATH(libffmpeg_bin_dir)
    822             add_to_PATH(x264_bin_dir)
    823             add_to_keywords(kw, 'include_dirs', x264_include_dir)
    824             add_to_keywords(kw, 'libraries', "libx264")
    825             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % x264_lib_dir)
    826             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    827             checkdirs(x264_include_dir, x264_lib_dir)
    828         elif "x265" in pkgs_options[0]:
    829             add_to_PATH(libffmpeg_bin_dir)
    830             add_to_PATH(x265_bin_dir)
    831             add_to_keywords(kw, 'include_dirs', x265_include_dir)
    832             add_to_keywords(kw, 'libraries', "libx265")
    833             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % x265_lib_dir)
    834             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    835             checkdirs(x265_include_dir, x265_lib_dir)
    836         elif "vpx" in pkgs_options[0]:
    837             add_to_PATH(libffmpeg_bin_dir)
    838             add_to_keywords(kw, 'include_dirs', vpx_include_dir)
    839             add_to_keywords(kw, 'libraries', *vpx_lib_names)
    840             add_to_keywords(kw, 'extra_link_args', "/NODEFAULTLIB:LIBCMT")
    841             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % vpx_lib_dir)
    842             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    843             checkdirs(vpx_include_dir, vpx_lib_dir)
    844         elif "webp" in pkgs_options[0]:
    845             add_to_PATH(webp_bin_dir)
    846             add_to_keywords(kw, 'include_dirs', webp_include_dir)
    847             add_to_keywords(kw, 'libraries', *webp_lib_names)
    848             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % webp_lib_dir)
    849             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    850             checkdirs(webp_include_dir, webp_lib_dir, webp_bin_dir)
    851         elif "pygobject-2.0" in pkgs_options[0]:
    852             dirs = (python_include_path,
    853                     pygtk_include_dir, atk_include_dir, gtk2_include_dir,
    854                     gtk2_base_include_dir, gdkconfig_include_dir, gdkpixbuf_include_dir,
    855                     glib_include_dir, glibconfig_include_dir,
    856                     cairo_include_dir, pango_include_dir)
    857             add_to_keywords(kw, 'include_dirs', *dirs)
    858             checkdirs(*dirs)
    859         elif "cuda" in pkgs_options[0]:
    860             add_to_keywords(kw, 'include_dirs', cuda_include_dir)
    861             checkdirs(cuda_include_dir)
    862             data_files.append(('.', glob.glob("%s/*32*.dll" % cuda_bin_dir)))
    863         else:
    864             sys.exit("ERROR: unknown package config: %s" % str(pkgs_options))
    865         if debug_ENABLED:
    866             #Od will override whatever may be specified elsewhere
    867             #and allows us to use the debug switches,
    868             #at the cost of a warning...
    869             for flag in ('/Od', '/Zi', '/DEBUG', '/RTC1', '/GS'):
    870                 add_to_keywords(kw, 'extra_compile_args', flag)
    871             add_to_keywords(kw, 'extra_link_args', "/DEBUG")
    872             kw['cython_gdb'] = True
    873         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    874         return kw
    875 
    876     import py2exe    #@UnresolvedImport
    877     assert py2exe is not None
    878 
    879     #with py2exe, we don't use py_modules, we use "packages"... sigh
    880     #(and it is a little bit different too - see below)
    881     del setup_options["py_modules"]
    882     add_packages("xpra.platform.win32")
     1090    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1091    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
     1092    if modules_ENABLED:
     1093        add_packages("xpra.platform.win32", "xpra.platform.win32.namedpipes")
    8831094    remove_packages("xpra.platform.darwin", "xpra.platform.xposix")
    884     #UI applications (detached from shell: no text output if ran from cmd.exe)
    885     setup_options["windows"] = [
    886                     {'script': 'scripts/xpra',                          'icon_resources': [(1, "win32/xpra_txt.ico")],  "dest_base": "Xpra",},
    887                     {'script': 'scripts/xpra_launcher',                 'icon_resources': [(1, "win32/xpra.ico")],      "dest_base": "Xpra-Launcher",},
    888                     {'script': 'xpra/gtk_common/gtk_view_keyboard.py',  'icon_resources': [(1, "win32/keyboard.ico")],  "dest_base": "GTK_Keyboard_Test",},
    889                     {'script': 'xpra/gtk_common/gtk_view_clipboard.py', 'icon_resources': [(1, "win32/clipboard.ico")], "dest_base": "GTK_Clipboard_Test",},
    890               ]
    891     #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
    892     console = [
    893                     {'script': 'scripts/xpra',                          'icon_resources': [(1, "win32/xpra_txt.ico")],  "dest_base": "Xpra_cmd",},
    894                     {'script': 'win32/python_execfile.py',              'icon_resources': [(1, "win32/python.ico")],    "dest_base": "Python_execfile",},
    895                     {'script': 'xpra/platform/win32/gui.py',            'icon_resources': [(1, "win32/loop.ico")],      "dest_base": "Events_Test",},
    896                     {'script': 'xpra/codecs/loader.py',                 'icon_resources': [(1, "win32/encoding.ico")],  "dest_base": "Encoding_info",},
    897                     {'script': 'xpra/sound/gstreamer_util.py',          'icon_resources': [(1, "win32/gstreamer.ico")], "dest_base": "GStreamer_info",},
    898                     {'script': 'xpra/sound/src.py',                     'icon_resources': [(1, "win32/microphone.ico")],"dest_base": "Sound_Record",},
    899                     {'script': 'xpra/sound/sink.py',                    'icon_resources': [(1, "win32/speaker.ico")],   "dest_base": "Sound_Play",},
    900               ]
    901     if opengl_ENABLED:
    902         console.append({'script': 'xpra/client/gl/gl_check.py',            'icon_resources': [(1, "win32/opengl.ico")],    "dest_base": "OpenGL_check",})
    903     setup_options["console"] = console
    904 
    905     py2exe_includes = external_includes + ["win32con", "win32gui", "win32process", "win32api"]
    906     dll_excludes = ["w9xpopen.exe","tcl85.dll", "tk85.dll"]
     1095
     1096    #this is where the win32 gi installer will put things:
     1097    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1098
     1099    #only add the cx_freeze specific options
     1100    #only if we are packaging:
     1101    if "install_exe" in sys.argv:
     1102        #with cx_freeze, we don't use py_modules
     1103        del setup_options["py_modules"]
     1104        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1105        if not hasattr(sys, "base_prefix"):
     1106            #workaround for broken sqlite hook with python 2.7, see:
     1107            #https://github.com/anthony-tuininga/cx_Freeze/pull/272
     1108            sys.base_prefix = sys.prefix
     1109
     1110        #cx_freeze doesn't use "data_files"...
     1111        del setup_options["data_files"]
     1112        #it wants source files first, then where they are placed...
     1113        #one item at a time (no lists)
     1114        #all in its own structure called "include_files" instead of "data_files"...
     1115        def add_data_files(target_dir, files):
     1116            if verbose_ENABLED:
     1117                print("add_data_files(%s, %s)" % (target_dir, files))
     1118            assert isinstance(target_dir, str)
     1119            assert isinstance(files, (list, tuple))
     1120            for f in files:
     1121                target_file = os.path.join(target_dir, os.path.basename(f))
     1122                data_files.append((f, target_file))
     1123
     1124        #pass a potentially nested dictionary representing the tree
     1125        #of files and directories we do want to include
     1126        #relative to gnome_include_path
     1127        def add_dir(base, defs):
     1128            if verbose_ENABLED:
     1129                print("add_dir(%s, %s)" % (base, defs))
     1130            if isinstance(defs, (list, tuple)):
     1131                for sub in defs:
     1132                    if isinstance(sub, dict):
     1133                        add_dir(base, sub)
     1134                    else:
     1135                        assert isinstance(sub, str)
     1136                        filename = os.path.join(gnome_include_path, base, sub)
     1137                        if os.path.exists(filename):
     1138                            add_data_files(base, [filename])
     1139                        else:
     1140                            print("Warning: missing '%s'" % filename)
     1141            else:
     1142                assert isinstance(defs, dict)
     1143                for d, sub in defs.items():
     1144                    assert isinstance(sub, (dict, list, tuple))
     1145                    #recurse down:
     1146                    add_dir(os.path.join(base, d), sub)
     1147
     1148        #convenience method for adding GI libs and "typelib" and "gir":
     1149        def add_gi(*libs):
     1150            if verbose_ENABLED:
     1151                print("add_gi(%s)" % str(libs))
     1152            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1153            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1154
     1155        def add_DLLs(*dll_names):
     1156            try:
     1157                do_add_DLLs(*dll_names)
     1158            except Exception as e:
     1159                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1160                print(" %s" % e)
     1161                sys.exit(1)
     1162
     1163        def do_add_DLLs(*dll_names):
     1164            dll_names = list(dll_names)
     1165            dll_files = []
     1166            import re
     1167            version_re = re.compile(r"-[0-9\.-]+$")
     1168            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1169            if os.path.exists(gnome_include_path):
     1170                dirs.insert(0, gnome_include_path)
     1171            if verbose_ENABLED:
     1172                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1173            for d in dirs:
     1174                if not os.path.exists(d):
     1175                    continue
     1176                for x in os.listdir(d):
     1177                    dll_path = os.path.join(d, x)
     1178                    x = x.lower()
     1179                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1180                        continue
     1181                    #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1182                    nameversion = x[3:-4]
     1183                    if verbose_ENABLED:
     1184                        print("checking %s: %s" % (x, nameversion))
     1185                    m = version_re.search(nameversion)          #look for version part of filename
     1186                    if m:
     1187                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1188                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1189                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1190                    else:
     1191                        dll_version = ""                        #no version
     1192                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1193                    if dll_name in dll_names:
     1194                        #this DLL is on our list
     1195                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1196                        dll_files.append(dll_path)
     1197                        dll_names.remove(dll_name)
     1198            if dll_names:
     1199                print("some DLLs could not be found:")
     1200                for x in dll_names:
     1201                    print(" - lib%s*.dll" % x)
     1202            add_data_files("", dll_files)
     1203
     1204        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1205        #(ie: "libatk-1.0-0.dll" -> "atk")
     1206        if sound_ENABLED or gtk3_ENABLED:
     1207            add_DLLs('gio', 'girepository', 'glib',
     1208                     'gnutls', 'gobject', 'gthread',
     1209                     'orc', 'stdc++',
     1210                     'winpthread',
     1211                     )
     1212        if gtk3_ENABLED:
     1213            add_DLLs('atk',
     1214                     'dbus', 'dbus-glib',
     1215                     'gdk', 'gdk_pixbuf', 'gtk',
     1216                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1217                     'harfbuzz', 'harfbuzz-gobject',
     1218                     'jasper', 'epoxy',
     1219                     'intl',
     1220                     'p11-kit',
     1221                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1222            #these are missing in newer aio installers (sigh):
     1223            do_add_DLLs('javascriptcoregtk')
     1224            if opengl_ENABLED:
     1225                do_add_DLLs('gdkglext', 'gtkglext')
     1226
     1227        if gtk3_ENABLED:
     1228            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1229            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1230                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1231            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1232                              "p11-kit", "xml",
     1233                              {"icons"  : ["hicolor"]},
     1234                              {"locale" : ["en"]},
     1235                              {"themes" : ["Default"]}
     1236                             ])
     1237        if gtk3_ENABLED or sound_ENABLED:
     1238            #causes warnings:
     1239            #add_dir('lib', ["gio"])
     1240            packages.append("gi")
     1241            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1242                   "GObject-2.0")
     1243        if gtk3_ENABLED:
     1244            add_gi("Atk-1.0",
     1245                   "Notify-0.7",
     1246                   "fontconfig-2.0", "freetype2-2.0",
     1247                   "GDesktopEnums-3.0", "Soup-2.4",
     1248                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0",
     1249                   "HarfBuzz-0.0",
     1250                   "Libproxy-1.0", "libxml2-2.0",
     1251                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1252                   "Rsvg-2.0",
     1253                   "win32-1.0")
     1254            if opengl_ENABLED:
     1255                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1256            add_DLLs('visual', 'curl', 'soup', 'openjpeg')
     1257        if server_ENABLED and not PYTHON3:
     1258            add_DLLs('sqlite3')
     1259
     1260        if gtk2_ENABLED:
     1261            add_dir('lib',      {
     1262                "gdk-pixbuf-2.0":    {
     1263                    "2.10.0"    :   {
     1264                        "loaders"   :
     1265                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp", "png",)]
     1266                        },
     1267                    },
     1268                })
     1269            if opengl_ENABLED:
     1270                add_DLLs("gtkglext-win32", "gdkglext-win32")
     1271            add_DLLs("gtk-win32", "gdk-win32",
     1272                     "gdk_pixbuf", "pyglib-2.0-python2")
     1273
     1274        if client_ENABLED:
     1275            #svg pixbuf loader:
     1276            add_DLLs("rsvg", "croco")
     1277
     1278        if sound_ENABLED:
     1279            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1280            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1281                   "GstTag-1.0")
     1282            add_DLLs('gstreamer', 'orc-test')
     1283            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1284                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1285                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1286                      "basecamerabinsrc", "mpegts", "photography",
     1287                      ):
     1288                add_DLLs('gst%s' % p)
     1289            #DLLs needed by the plugins:
     1290            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1291            #add the gstreamer plugins we need:
     1292            GST_PLUGINS = ("app",
     1293                           "cutter",
     1294                           #muxers:
     1295                           "gdp", "matroska", "ogg", "isomp4",
     1296                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1297                           "coreelements", "directsound", "directsoundsink", "directsoundsrc", "wasapi",
     1298                           #codecs:
     1299                           "opus", "opusparse", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1300                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1301                           "autodetect",
     1302                           #untested: a52dec, voaacenc
     1303                           )
     1304            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1305            #END OF SOUND
     1306
     1307        if server_ENABLED:
     1308            #used by proxy server:
     1309            external_includes += ["multiprocessing", "setproctitle"]
     1310
     1311        external_includes += ["encodings"]
     1312        if client_ENABLED:
     1313            #for parsing "open-command":
     1314            external_includes += ["shlex"]
     1315            #for version check:
     1316            external_includes += [
     1317                                  "ftplib", "fileinput",
     1318                                  ]
     1319            if PYTHON3:
     1320                external_includes += ["urllib", "http.cookiejar", "http.client"]
     1321            else:
     1322                external_includes += ["urllib2", "cookielib", "httplib"]
     1323
     1324        if PYTHON3:
     1325            #hopefully, cx_Freeze will fix this horror:
     1326            #(we shouldn't have to deal with DLL dependencies)
     1327            import site
     1328            lib_python = os.path.dirname(site.getsitepackages()[0])
     1329            lib_dynload_dir = os.path.join(lib_python, "lib-dynload")
     1330            add_data_files('', glob.glob("%s/zlib*dll" % lib_dynload_dir))
     1331            for x in ("io", "codecs", "abc", "_weakrefset", "encodings"):
     1332                add_data_files("lib/", glob.glob("%s/%s*" % (lib_python, x)))
     1333        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1334        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1335        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL"]
     1336        cx_freeze_options = {
     1337                            "includes"          : external_includes,
     1338                            "packages"          : packages,
     1339                            "include_files"     : data_files,
     1340                            "excludes"          : excludes,
     1341                            "include_msvcr"     : True,
     1342                            "bin_excludes"      : bin_excludes,
     1343                            }
     1344        #cx_Freeze v5 workarounds:
     1345        if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
     1346            add_packages("numpy.core._methods", "numpy.lib.format")
     1347
     1348        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1349        executables = []
     1350        setup_options["executables"] = executables
     1351
     1352        def add_exe(script, icon, base_name, base="Console"):
     1353            executables.append(Executable(
     1354                        script                  = script,
     1355                        initScript              = None,
     1356                        #targetDir               = "dist",
     1357                        icon                    = "icons/%s" % icon,
     1358                        targetName              = "%s.exe" % base_name,
     1359                        base                    = base,
     1360                        ))
     1361
     1362        def add_console_exe(script, icon, base_name):
     1363            add_exe(script, icon, base_name)
     1364        def add_gui_exe(script, icon, base_name):
     1365            add_exe(script, icon, base_name, base="Win32GUI")
     1366        def add_service_exe(script, icon, base_name):
     1367            add_exe(script, icon, base_name, base="Win32Service")
     1368
     1369        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1370        if (client_ENABLED or server_ENABLED) and (gtk2_ENABLED or gtk3_ENABLED):
     1371            add_gui_exe("scripts/xpra",                         "xpra.ico",         "Xpra")
     1372            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1373            add_console_exe("scripts/xpra_launcher",            "xpra.ico",         "Xpra-Launcher-Debug")
     1374            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1375            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1376            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1377        if server_ENABLED:
     1378            add_gui_exe("scripts/auth_dialog",                  "authentication.ico", "Auth_Dialog")
     1379        if gtk2_ENABLED:
     1380            #these need porting..
     1381            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1382        if mdns_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1383            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1384        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1385        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1386        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1387        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1388        if gtk2_ENABLED or gtk3_ENABLED:
     1389            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1390            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1391            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1392            add_gui_exe("xpra/client/gtk_base/example/tray.py", "xpra.ico",         "SystemTray_Test")
     1393            add_gui_exe("xpra/client/gtk_base/u2f_tool.py",     "authentication.ico", "U2F_Tool")
     1394        if client_ENABLED or server_ENABLED:
     1395            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1396            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1397        if server_ENABLED:
     1398            add_console_exe("xpra/server/auth/sqlite_auth.py",  "sqlite.ico",        "SQLite_auth_tool")
     1399            add_console_exe("xpra/server/auth/win32_auth.py",   "authentication.ico", "System-Auth-Test")
     1400            add_console_exe("xpra/server/auth/ldap_auth.py",    "authentication.ico", "LDAP-Auth-Test")
     1401            add_console_exe("xpra/server/auth/ldap3_auth.py",   "authentication.ico", "LDAP3-Auth-Test")
     1402            add_console_exe("win32/service/proxy.py",           "xpra_txt.ico",      "Xpra-Proxy")
     1403            add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico",     "System-Logon-Test")
     1404        if client_ENABLED:
     1405            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1406            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1407            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1408        if client_ENABLED:
     1409            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1410            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1411        if sound_ENABLED:
     1412            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1413            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1414            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1415            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1416            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1417        if opengl_ENABLED:
     1418            if PYTHON3:
     1419                add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1420            else:
     1421                add_console_exe("xpra/client/gl/gtk_base/gtkgl_check.py", "opengl.ico", "OpenGL_check")
     1422        if webcam_ENABLED:
     1423            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1424            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1425        if printing_ENABLED:
     1426            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1427            add_console_exe("xpra/platform/win32/pdfium.py",    "printer.ico",     "PDFIUM_Print")
     1428            add_DLLs("pdfium")  #libpdfium.dll
     1429        if nvenc_ENABLED:
     1430            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1431        if nvfbc_ENABLED:
     1432            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1433        if nvfbc_ENABLED or nvenc_ENABLED:
     1434            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1435
     1436        if example_ENABLED:
     1437            add_gui_exe("xpra/client/gtk_base/example/colors.py",               "encoding.ico",     "Colors")
     1438            add_gui_exe("xpra/client/gtk_base/example/colors_gradient.py",      "encoding.ico",     "Colors-Gradient")
     1439            if not PYTHON3:
     1440                add_gui_exe("xpra/client/gtk_base/example/gl_colors_gradient.py",   "encoding.ico",     "OpenGL-Colors-Gradient")
     1441            add_gui_exe("xpra/client/gtk_base/example/colors_plain.py",         "encoding.ico",     "Colors-Plain")
     1442            add_gui_exe("xpra/client/gtk_base/example/bell.py",                 "bell.ico",         "Bell")
     1443            add_gui_exe("xpra/client/gtk_base/example/transparent_colors.py",   "transparent.ico",  "Transparent-Colors")
     1444            add_gui_exe("xpra/client/gtk_base/example/transparent_window.py",   "transparent.ico",  "Transparent-Window")
     1445            add_gui_exe("xpra/client/gtk_base/example/fontrendering.py",        "font.ico",         "Font-Rendering")
     1446
     1447    if ("install_exe" in sys.argv) or ("install" in sys.argv):
     1448        #FIXME: how do we figure out what target directory to use?
     1449        print("calling build_xpra_conf in-place")
     1450        #building etc files in-place:
     1451        if data_ENABLED:
     1452            build_xpra_conf(".")
     1453            add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1454            add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1455            add_data_files('etc/xpra', glob.glob("etc/xpra/nvfbc*.keys"))
     1456            add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1457        #build minified html5 client in temporary build dir:
     1458        if "clean" not in sys.argv and html5_ENABLED:
     1459            install_html5(os.path.join(install, "www"), )
     1460            for k,v in glob_recurse("build/www").items():
     1461                if k!="":
     1462                    k = os.sep+k
     1463                add_data_files('www'+k, v)
     1464
     1465    if data_ENABLED:
     1466        add_data_files(share_xpra,              ["win32/website.url"])
     1467        add_data_files('%s/icons' % share_xpra,  glob.glob('icons\\*.ico'))
     1468        add_data_files(share_xpra,              ["win32\\DirectShow.tlb"])
     1469
    9071470    remove_packages(*external_excludes)
     1471    external_includes.append("pyu2f")
     1472    external_includes.append("mmap")
     1473    external_includes.append("comtypes")    #used by webcam and netdev_query
     1474    remove_packages("comtypes.gen")         #this is generated at runtime
     1475                                            #but we still have to remove the empty directory by hand
     1476                                            #afterwards because cx_freeze does weird things (..)
    9081477    remove_packages(#not used on win32:
    909                     "mmap",
    9101478                    #we handle GL separately below:
    9111479                    "OpenGL", "OpenGL_accelerate",
     
    9131481                    "ctypes.macholib")
    9141482
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1483    if webcam_ENABLED and False:
     1484        external_includes.append("cv2")
     1485    else:
     1486        remove_packages("cv2")
     1487
     1488    if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
    9161489        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1490        external_includes.append("numpy")
    9181491    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1492        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211493                        "pydoc")
    9221494
    923     if sound_ENABLED:
    924         py2exe_includes += ["pygst", "gst", "gst.extend"]
    925     else:
    926         remove_packages("pygst", "gst", "gst.extend")
    927 
    928     if opengl_ENABLED:
     1495    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1496    remove_packages("pygst", "gst", "gst.extend")
     1497
     1498    #add subset of PyOpenGL modules (only when installing):
     1499    if opengl_ENABLED and "install_exe" in sys.argv:
    9291500        #for this hack to work, you must add "." to the sys.path
    9301501        #so python can load OpenGL from the install directory
    9311502        #(further complicated by the fact that "." is the "frozen" path...)
    932         import OpenGL, OpenGL_accelerate        #@UnresolvedImport
    933         import shutil
    934         print("*** copy PyOpenGL modules ***")
    935         for module_name, module in {"OpenGL" : OpenGL, "OpenGL_accelerate" : OpenGL_accelerate}.items():
     1503        #but we re-add those two directories to the library.zip as part of the build script
     1504        import OpenGL
     1505        print("*** copying PyOpenGL modules to %s ***" % install)
     1506        glmodules = {
     1507            "OpenGL" : OpenGL,
     1508            }
     1509        try:
     1510            import OpenGL_accelerate        #@UnresolvedImport
     1511        except ImportError as e:
     1512            print("Warning: missing OpenGL_accelerate module")
     1513            print(" %s" % e)
     1514        else:
     1515            glmodules["OpenGL_accelerate"] = OpenGL_accelerate
     1516        for module_name, module in glmodules.items():
    9361517            module_dir = os.path.dirname(module.__file__ )
    9371518            try:
    9381519                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1520                    module_dir, os.path.join(install, "lib", module_name),
     1521                    ignore = shutil.ignore_patterns(
     1522                        "Tk", "AGL", "EGL",
     1523                        "GLX", "GLX.*", "_GLX.*",
     1524                        "GLE", "GLES1", "GLES2", "GLES3",
     1525                        )
    9411526                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1527                print("copied %s to %s/%s" % (module_dir, install, module_name))
     1528            except Exception as e:
     1529                if not isinstance(e, WindowsError) or ("already exists" not in str(e)): #@UndefinedVariable
    9441530                    raise
    945     py2exe_options = {
    946                       "skip_archive"   : False,
    947                       "optimize"       : 0,    #WARNING: do not change - causes crashes
    948                       "unbuffered"     : True,
    949                       "compressed"     : True,
    950                       "skip_archive"   : False,
    951                       "packages"       : packages,
    952                       "includes"       : py2exe_includes,
    953                       "excludes"       : excludes,
    954                       "dll_excludes"   : dll_excludes,
    955                      }
    956     setup_options["options"] = {"py2exe" : py2exe_options}
    957     data_files += [
    958                    ('', ['COPYING', 'README',
    959                          'win32/website.url',
    960                          'etc/xpra/client-only/xpra.conf'] +
    961                          glob.glob('%s\\bin\\*.dll' % libffmpeg_path)),
    962                    ('icons', glob.glob('win32\\*.ico') + glob.glob('icons\\*.*')),
    963                    ('Microsoft.VC90.CRT', glob.glob('%s\\Microsoft.VC90.CRT\\*.*' % C_DLLs)),
    964                    ('Microsoft.VC90.MFC', glob.glob('%s\\Microsoft.VC90.MFC\\*.*' % C_DLLs)),
    965                    ]
    966     if enc_x264_ENABLED:
    967         data_files.append(('', ['%s\\libx264.dll' % x264_bin_dir]))
    968     html5_dir = ''
    969 
    970     if webm_ENABLED or webp_ENABLED:
    971         #Note: confusingly, the python bindings are called webm...
    972         #add the webp DLL to the output:
    973         #And since 0.2.1, you have to compile the DLL yourself..
    974         #the path after installing may look like this:
    975         #webp_DLL = "C:\\libwebp-0.3.1-windows-x86\\bin\\libwebp.dll"
    976         #but we use something more generic, without the version numbers:
    977         webp_DLL = webp_bin_dir+"\\libwebp.dll"
    978         data_files.append(('', [webp_DLL]))
    979         #and its license:
    980         data_files.append(('webm', ["xpra/codecs/webm/LICENSE"]))
    981 
    982 
     1531
     1532        add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1533
     1534    #END OF win32
    9831535#*******************************************************************************
    9841536else:
    9851537    #OSX and *nix:
    986     scripts += ["scripts/xpra", "scripts/xpra_launcher"]
    987     data_files += [
    988                     ("share/man/man1",      ["man/xpra.1", "man/xpra_launcher.1"]),
    989                     ("share/xpra",          ["README", "COPYING"]),
    990                     ("share/xpra/icons",    glob.glob("icons/*")),
    991                     ("share/applications",  ["xdg/xpra_launcher.desktop", "xdg/xpra.desktop"]),
    992                     ("share/icons",         ["xdg/xpra.png"])
    993                   ]
    994     html5_dir = "share/xpra/www"
    995     if webm_ENABLED:
    996         data_files.append(('share/xpra/webm', ["xpra/codecs/webm/LICENSE"]))
     1538    if LINUX:
     1539        if scripts_ENABLED:
     1540            scripts += ["scripts/xpra_udev_product_version", "scripts/xpra_signal_listener"]
     1541        libexec_scripts = []
     1542        if is_Fedora() or is_CentOS():
     1543            libexec = "libexec"
     1544        else:
     1545            libexec = "lib"
     1546        if xdg_open_ENABLED:
     1547            libexec_scripts += ["scripts/xdg-open", "scripts/gnome-open", "scripts/gvfs-open"]
     1548        if server_ENABLED:
     1549            libexec_scripts.append("scripts/auth_dialog")
     1550        if libexec_scripts:
     1551            add_data_files("%s/xpra/" % libexec, libexec_scripts)
     1552    if data_ENABLED:
     1553        man_path = "share/man"
     1554        if OPENBSD:
     1555            man_path = "man"
     1556        add_data_files("%s/man1" % man_path,  ["man/xpra.1", "man/xpra_launcher.1"])
     1557        add_data_files("share/applications",  glob.glob("xdg/*.desktop"))
     1558        add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1559        add_data_files("share/icons",         ["xdg/xpra.png", "xdg/xpra-mdns.png", "xdg/xpra-shadow.png"])
     1560        add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1561
     1562    #here, we override build and install so we can
     1563    #generate our /etc/xpra/xpra.conf
     1564    class build_override(build):
     1565        def run(self):
     1566            build.run(self)
     1567            self.run_command("build_conf")
     1568
     1569    class build_conf(build):
     1570        def run(self):
     1571            try:
     1572                build_base = self.distribution.command_obj['build'].build_base
     1573            except:
     1574                build_base = self.build_base
     1575            build_xpra_conf(build_base)
     1576
     1577    class install_data_override(install_data):
     1578        def run(self):
     1579            print("install_data_override: install_dir=%s" % self.install_dir)
     1580            if html5_ENABLED:
     1581                install_html5(os.path.join(self.install_dir, "%s/www" % share_xpra))
     1582            install_data.run(self)
     1583
     1584            root_prefix = self.install_dir.rstrip("/")
     1585            if root_prefix.endswith("/usr"):
     1586                root_prefix = root_prefix[:-4]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1587            build_xpra_conf(root_prefix)
     1588
     1589            def copytodir(src, dst_dir, dst_name=None, chmod=0o644):
     1590                #convert absolute paths:
     1591                if dst_dir.startswith("/"):
     1592                    dst_dir = root_prefix+dst_dir
     1593                else:
     1594                    dst_dir = self.install_dir.rstrip("/")+"/"+dst_dir
     1595                #make sure the target directory exists:
     1596                self.mkpath(dst_dir)
     1597                #generate the target filename:
     1598                filename = os.path.basename(src)
     1599                dst_file = os.path.join(dst_dir, dst_name or filename)
     1600                #copy it
     1601                print("copying %s -> %s (%s)" % (src, dst_dir, oct(chmod)))
     1602                shutil.copyfile(src, dst_file)
     1603                if chmod:
     1604                    os.chmod(dst_file, chmod)
     1605
     1606            if printing_ENABLED and POSIX:
     1607                #install "/usr/lib/cups/backend" with 0700 permissions:
     1608                copytodir("cups/xpraforwarder", "lib/cups/backend", chmod=0o700)
     1609
     1610            if x11_ENABLED:
     1611                #install xpra_Xdummy if we need it:
     1612                xvfb_command = detect_xorg_setup()
     1613                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])) or Xdummy_wrapper_ENABLED is True:
     1614                    copytodir("scripts/xpra_Xdummy", "bin", chmod=0o755)
     1615                #install xorg*.conf, cuda.conf and nvenc.keys:
     1616                etc_xpra_files = ["xorg.conf"]
     1617                if uinput_ENABLED:
     1618                    etc_xpra_files.append("xorg-uinput.conf")
     1619                if nvenc_ENABLED or nvfbc_ENABLED:
     1620                    etc_xpra_files.append("cuda.conf")
     1621                if nvenc_ENABLED:
     1622                    etc_xpra_files.append("nvenc.keys")
     1623                if nvfbc_ENABLED:
     1624                    etc_xpra_files.append("nvfbc.keys")
     1625                for x in etc_xpra_files:
     1626                    copytodir("etc/xpra/%s" % x, "/etc/xpra")
     1627                copytodir("etc/X11/xorg.conf.d/90-xpra-virtual.conf", "/etc/X11/xorg.conf.d/")
     1628
     1629            if pam_ENABLED:
     1630                copytodir("etc/pam.d/xpra", "/etc/pam.d")
     1631
     1632            systemd_dir = "/lib/systemd/system"
     1633            if service_ENABLED:
     1634                #Linux init service:
     1635                if os.path.exists("/bin/systemctl"):
     1636                    if sd_listen_ENABLED:
     1637                        copytodir("service/xpra.service", systemd_dir)
     1638                    else:
     1639                        copytodir("service/xpra-nosocketactivation.service", systemd_dir, dst_name="xpra.service")
     1640                else:
     1641                    copytodir("service/xpra", "/etc/init.d")
     1642                if os.path.exists("/etc/sysconfig"):
     1643                    copytodir("etc/sysconfig/xpra", "/etc/sysconfig")
     1644                elif os.path.exists("/etc/default"):
     1645                    copytodir("etc/sysconfig/xpra", "/etc/default")
     1646            if sd_listen_ENABLED:
     1647                copytodir("service/xpra.socket", systemd_dir)
     1648            if dbus_ENABLED and proxy_ENABLED:
     1649                copytodir("dbus/xpra.conf", "/etc/dbus-1/system.d")
     1650
     1651
     1652    # add build_conf to build step
     1653    cmdclass.update({
     1654             'build'        : build_override,
     1655             'build_conf'   : build_conf,
     1656             'install_data' : install_data_override,
     1657             })
    9971658
    9981659    if OSX:
     1660        #pyobjc needs email.parser
     1661        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
     1662        external_includes += ["kerberos", "future", "pyu2f", "paramiko", "nacl"]
     1663        if not PYTHON3:
     1664            external_includes += ["urllib2"]
    9991665        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001666        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011667        add_packages("xpra.platform.darwin")
     1668        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1669        #for u2f on python2:
     1670        if not PYTHON3:
     1671            modules.append("UserList")
     1672            modules.append("UserString")
     1673        #to support GStreamer 1.x we need this:
     1674        modules.append("importlib")
    10021675    else:
    10031676        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041677        add_packages("xpra.platform.xposix")
    1005         #always include the wrapper in case we need it later:
    1006         #(we remove it during the 'install' step below if it isn't actually needed)
    1007         scripts.append("scripts/xpra_Xdummy")
     1678        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1679        if data_ENABLED:
     1680            #not supported by all distros, but doesn't hurt to install them anyway:
     1681            for x in ("tmpfiles.d", "sysusers.d"):
     1682                add_data_files("lib/%s" % x, ["%s/xpra.conf" % x])
     1683            if uinput_ENABLED:
     1684                add_data_files("lib/udev/rules.d/", ["udev/rules.d/71-xpra-virtual-pointer.rules"])
    10081685
    10091686    #gentoo does weird things, calls --no-compile with build *and* install
    10101687    #then expects to find the cython modules!? ie:
    1011     #> python2.7 setup.py build -b build-2.7 install --no-compile --root=/var/tmp/portage/x11-wm/xpra-0.7.0/temp/images/2.7
     1688    #> python2.7 setup.py build -b build-2.7 install --no-compile \
     1689    # --root=/var/tmp/portage/x11-wm/xpra-0.7.0/temp/images/2.7
    10121690    #otherwise we use the flags to skip pkgconfig
    10131691    if ("--no-compile" in sys.argv or "--skip-build" in sys.argv) and not ("build" in sys.argv and "install" in sys.argv):
    1014         def pkgconfig(*pkgs_options, **ekw):
    1015             return {}
    1016     if "install" in sys.argv:
    1017         #prepare default [/usr/local]/etc configuration files:
    1018         if sys.prefix == '/usr':
    1019             etc_prefix = '/etc/xpra'
    1020         else:
    1021             etc_prefix = sys.prefix + '/etc/xpra'
    1022 
    1023         etc_files = []
    1024         if server_ENABLED and x11_ENABLED:
    1025             etc_files = ["etc/xpra/xorg.conf"]
    1026             #figure out the version of the Xorg server:
    1027             xorg_conf, use_Xdummy_wrapper = get_xorg_conf_and_script()
    1028             if not use_Xdummy_wrapper and "scripts/xpra_Xdummy" in scripts:
    1029                 #if we're not using the wrapper, don't install it
    1030                 scripts.remove("scripts/xpra_Xdummy")
    1031             etc_files.append(xorg_conf)
    1032         data_files.append((etc_prefix, etc_files))
     1692        pkgconfig = no_pkgconfig
    10331693
    10341694    if OSX and "py2app" in sys.argv:
     
    10391699        del setup_options["py_modules"]
    10401700        scripts = []
    1041         def cython_add(*args, **kwargs):
     1701        def cython_add(*_args, **_kwargs):
    10421702            pass
    10431703
     
    10451705        remove_packages(*external_excludes)
    10461706
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1707        try:
     1708            from xpra.src_info import REVISION
     1709        except ImportError:
     1710            REVISION = "unknown"
     1711        Plist = {
     1712            "CFBundleDocumentTypes" : {
     1713                "CFBundleTypeExtensions"    : ["Xpra"],
     1714                "CFBundleTypeName"          : "Xpra Session Config File",
     1715                "CFBundleName"              : "Xpra",
     1716                "CFBundleTypeRole"          : "Viewer",
     1717                },
     1718            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2018 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1719            "CFBundleIdentifier"            : "org.xpra.xpra",
     1720            }
    10531721        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541722        #so the make-app.sh script still has to hack around this problem.
    10551723        add_modules(*external_includes)
     1724        #needed by python-lz4:
     1725        add_modules("distutils")
    10561726        py2app_options = {
    10571727            'iconfile'          : '../osx/xpra.icns',
     
    10651735            }
    10661736        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1737        setup_options["app"]     = ["xpra/scripts/main.py"]
     1738
     1739    if OSX:
     1740        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1741        #so instead we have to query each package seperately and merge the results:
     1742        def osx_pkgconfig(*pkgs_options, **ekw):
     1743            kw = dict(ekw)
     1744            for pkg in pkgs_options:
     1745                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1746                if pkg.lower().startswith("x"):
     1747                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1748                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1749                kw = exec_pkgconfig(pkg, **kw)
     1750                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1751            return kw
     1752
     1753        pkgconfig = osx_pkgconfig
     1754
     1755
     1756if scripts_ENABLED:
     1757    scripts += ["scripts/xpra", "scripts/xpra_launcher"]
     1758
     1759if data_ENABLED:
     1760    add_data_files(share_xpra,                      ["README", "COPYING"])
     1761    add_data_files(share_xpra,                      ["bell.wav"])
     1762    add_data_files("%s/http-headers" % share_xpra,   glob.glob("http-headers/*"))
     1763    add_data_files("%s/icons" % share_xpra,          glob.glob("icons/*png"))
     1764    add_data_files("%s/content-type" % share_xpra,   glob.glob("content-type/*"))
     1765    add_data_files("%s/content-categories" % share_xpra, glob.glob("content-categories/*"))
    10681766
    10691767
    10701768if html5_ENABLED:
    1071     for k,v in glob_recurse("html5").items():
    1072         if (k!=""):
    1073             k = os.sep+k
    1074         data_files.append((html5_dir+k, v))
    1075 
     1769    if WIN32 or OSX:
     1770        external_includes.append("numpy")
     1771        external_includes.append("ssl")
     1772        external_includes.append("_ssl")
     1773        if not PYTHON3:
     1774            external_includes.append("mimetypes")
     1775            external_includes.append("mimetools")
     1776            external_includes.append("BaseHTTPServer")
     1777
     1778
     1779if annotate_ENABLED:
     1780    from Cython.Compiler import Options
     1781    Options.annotate = True
    10761782
    10771783
    10781784#*******************************************************************************
    1079 toggle_packages(server_ENABLED, "xpra.server", "xpra.server.stats", "xpra.server.auth")
    1080 toggle_packages(server_ENABLED or gtk2_ENABLED or gtk3_ENABLED, "xpra.gtk_common", "xpra.clipboard")
    1081 
    1082 
    1083 toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.gtk_x11", "xpra.x11.bindings")
     1785buffers_c = "xpra/buffers/buffers.c"
     1786memalign_c = "xpra/buffers/memalign.c"
     1787xxhash_c = "xpra/buffers/xxhash.c"
     1788membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1789
     1790if modules_ENABLED:
     1791    add_packages("xpra.buffers")
     1792    buffers_pkgconfig = pkgconfig(optimize=3)
     1793    if cython_ENABLED:
     1794        cython_add(Extension("xpra.buffers.membuf",
     1795                    ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1796
     1797
     1798toggle_packages(dbus_ENABLED, "xpra.dbus")
     1799toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1800toggle_packages(websockets_ENABLED, "xpra.net.websockets")
     1801toggle_packages(server_ENABLED or proxy_ENABLED, "xpra.server", "xpra.server.auth")
     1802toggle_packages(rfb_ENABLED, "xpra.server.rfb")
     1803toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1804toggle_packages(server_ENABLED, "xpra.server.window")
     1805toggle_packages(server_ENABLED or shadow_ENABLED, "xpra.server.mixins", "xpra.server.source")
     1806toggle_packages(shadow_ENABLED, "xpra.server.shadow")
     1807toggle_packages(server_ENABLED or client_ENABLED, "xpra.clipboard")
     1808toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1809toggle_packages(notifications_ENABLED, "xpra.notifications")
     1810
     1811#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1812if dbus_ENABLED and server_ENABLED:
     1813    add_packages("xpra.server.dbus")
     1814
     1815if OSX:
     1816    if PYTHON3:
     1817        quartz_pkgconfig = pkgconfig("gtk+-3.0", "pygobject-3.0")
     1818        add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1819                    "-ObjC",
     1820                    "-framework", "AppKit",
     1821                    "-I/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/")
     1822        cython_add(Extension("xpra.platform.darwin.gdk3_bindings",
     1823                ["xpra/platform/darwin/gdk3_bindings.pyx"],
     1824                language="objc",
     1825                **quartz_pkgconfig
     1826                ))
     1827    else:
     1828        quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1829        add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1830                    '-mmacosx-version-min=10.10',
     1831                    '-framework', 'Foundation',
     1832                    '-framework', 'AppKit',
     1833                    '-ObjC',
     1834                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h")
     1835        cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1836                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1837                language="objc",
     1838                **quartz_pkgconfig
     1839                ))
     1840
     1841if cython_ENABLED:
     1842    monotonic_time_pkgconfig = pkgconfig()
     1843    if not OSX and not WIN32 and not OPENBSD:
     1844        add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1845    cython_add(Extension("xpra.monotonic_time",
     1846                ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1847                **monotonic_time_pkgconfig
     1848                ))
     1849
     1850
     1851toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841852if x11_ENABLED:
    10851853    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1854    if gtk2_ENABLED:
     1855        make_constants("xpra", "x11", "constants", pxi_file="xpra/x11/gtk2/constants.pxi")
     1856    if gtk3_ENABLED:
     1857        make_constants("xpra", "x11", "constants", pxi_file="xpra/x11/gtk3/constants.pxi")
    10871858
    10881859    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981869                **pkgconfig("x11")
    10991870                ))
     1871    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1872                ["xpra/x11/bindings/posix_display_source.pyx"],
     1873                **pkgconfig("x11")
     1874                ))
     1875
    11001876    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011877                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041880    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051881                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1882                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071883                ))
    11081884
    11091885    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101886                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1887                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121888                ))
    11131889    cython_add(Extension("xpra.x11.bindings.ximage",
    11141890                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1891                **pkgconfig("x11", "xext", "xcomposite")
    11161892                ))
    1117 
    1118     #below uses gtk/gdk:
    1119     cython_add(Extension("xpra.x11.gtk_x11.gdk_display_source",
    1120                 ["xpra/x11/gtk_x11/gdk_display_source.pyx"],
    1121                 **pkgconfig(*PYGTK_PACKAGES)
     1893if xinput_ENABLED:
     1894    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1895                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1896                **pkgconfig("x11", "xi")
    11221897                ))
    1123     GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["xfixes", "xdamage"]
    1124     cython_add(Extension("xpra.x11.gtk_x11.gdk_bindings",
    1125                 ["xpra/x11/gtk_x11/gdk_bindings.pyx"],
    1126                 **pkgconfig(*GDK_BINDINGS_PACKAGES)
     1898
     1899toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1900toggle_packages(server_ENABLED and gtk_x11_ENABLED, "xpra.x11.models")
     1901if gtk_x11_ENABLED:
     1902    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1903    toggle_packages(not PYTHON3, "xpra.x11.gtk2")
     1904    if PYTHON3:
     1905        #GTK3 display source:
     1906        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1907                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1908                    **pkgconfig("gdk-3.0")
     1909                    ))
     1910        cython_add(Extension("xpra.x11.gtk3.gdk_bindings",
     1911                    ["xpra/x11/gtk3/gdk_bindings.pyx", "xpra/x11/gtk3/gdk_x11_macros.c"],
     1912                    **pkgconfig("gdk-3.0")
     1913                    ))
     1914
     1915    else:
     1916        #GTK2:
     1917        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1918                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1919                    **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1920                    ))
     1921        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1922        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1923                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1924                    **pkgconfig(*GDK_BINDINGS_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1925                    ))
     1926
     1927toggle_packages(not PYTHON3 and (gtk2_ENABLED or gtk_x11_ENABLED), "xpra.gtk_common.gtk2")
     1928if gtk2_ENABLED or (gtk_x11_ENABLED and not PYTHON3):
     1929    cython_add(Extension("xpra.gtk_common.gtk2.gdk_bindings",
     1930                ["xpra/gtk_common/gtk2/gdk_bindings.pyx"],
     1931                **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
    11271932                ))
    1128 
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1933elif gtk3_ENABLED or (gtk_x11_ENABLED and PYTHON3):
     1934    cython_add(Extension("xpra.gtk_common.gtk3.gdk_bindings",
     1935                ["xpra/gtk_common/gtk3/gdk_bindings.pyx"],
     1936                **pkgconfig("gtk+-3.0", "pygobject-3.0")
     1937                ))
     1938
     1939if client_ENABLED and gtk3_ENABLED:
     1940    #cairo workaround:
     1941    if OSX or is_Ubuntu() or is_Debian():
     1942        pycairo = "py3cairo"
     1943    else:
     1944        pycairo = "pycairo"
     1945    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1946                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1947                **pkgconfig(pycairo)
     1948                ))
     1949
     1950if client_ENABLED or server_ENABLED:
     1951    add_packages("xpra.codecs.argb")
     1952    argb_pkgconfig = pkgconfig(optimize=3)
    11321953    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1954                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1955
     1956
     1957#build tests, but don't install them:
     1958toggle_packages(tests_ENABLED, "unit")
    11341959
    11351960
    11361961if bundle_tests_ENABLED:
    11371962    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
    1139         if (k!=""):
     1963    for k,v in glob_recurse("unit").items():
     1964        if k!="":
    11401965            k = os.sep+k
    1141         data_files.append(("tests"+k, v))
    1142 
    1143 #special case for client: cannot use toggle_packages which would include gtk3, qt, etc:
     1966        add_data_files("unit"+k, v)
     1967
     1968#python-cryptography needs workarounds for bundling:
     1969if crypto_ENABLED and (OSX or WIN32):
     1970    external_includes.append("_ssl")
     1971    external_includes.append("cffi")
     1972    external_includes.append("_cffi_backend")
     1973    external_includes.append("cryptography")
     1974    external_includes.append("idna")
     1975    external_includes.append("idna.idnadata")
     1976    external_includes.append("pkg_resources._vendor.packaging")
     1977    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1978    external_includes.append("pkg_resources._vendor.pyparsing")
     1979    add_modules("cryptography.hazmat.bindings._openssl")
     1980    add_modules("cryptography.hazmat.bindings._constant_time")
     1981    add_modules("cryptography.hazmat.bindings._padding")
     1982    add_modules("cryptography.hazmat.backends.openssl")
     1983    add_modules("cryptography.fernet")
     1984    if WIN32:
     1985        external_includes.append("appdirs")
     1986
     1987#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441988if client_ENABLED:
    1145     add_modules("xpra.client", "xpra.client.notifications")
    1146 toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or server_ENABLED, "xpra.gtk_common")
     1989    add_modules("xpra.client", "xpra.client.mixins")
     1990    add_modules("xpra.scripts.gtk_info")
     1991    add_modules("xpra.scripts.show_webcam")
     1992if gtk2_ENABLED or gtk3_ENABLED:
     1993    add_modules("xpra.scripts.bug_report")
     1994toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471995toggle_packages(client_ENABLED and gtk2_ENABLED, "xpra.client.gtk2")
    1148 toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3", "gi")
    1149 toggle_packages(client_ENABLED and qt4_ENABLED, "xpra.client.qt4", "PyQt4")
     1996toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1997toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501998toggle_packages(client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED), "xpra.client.gtk_base")
    1151 toggle_packages(sound_ENABLED, "xpra.sound")
    1152 toggle_packages(webm_ENABLED, "xpra.codecs.webm")
    1153 toggle_packages(client_ENABLED and gtk2_ENABLED and opengl_ENABLED, "xpra.client.gl")
     1999toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     2000toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     2001toggle_packages(client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED) and example_ENABLED, "xpra.client.gtk_base.example")
     2002if client_ENABLED and WIN32 and MINGW_PREFIX:
     2003    propsys_pkgconfig = pkgconfig()
     2004    if debug_ENABLED:
     2005        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     2006    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     2007    cython_add(Extension("xpra.platform.win32.propsys",
     2008                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     2009                language="c++",
     2010                **propsys_pkgconfig))
     2011
     2012if client_ENABLED or server_ENABLED:
     2013    add_modules("xpra.codecs")
     2014toggle_packages(keyboard_ENABLED, "xpra.keyboard")
     2015if client_ENABLED or server_ENABLED:
     2016    add_modules(
     2017        "xpra.scripts.config",
     2018        "xpra.scripts.parsing",
     2019        "xpra.scripts.exec_util",
     2020        "xpra.scripts.fdproxy",
     2021        "xpra.scripts.version",
     2022        )
     2023if server_ENABLED or proxy_ENABLED:
     2024    add_modules("xpra.scripts.server")
     2025if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     2026    add_modules("xpra.scripts.gtk_info")
     2027
     2028toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     2029#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     2030#and tries to include both gtk3 and gtk2, and fails hard..
     2031for x in (
     2032    "gl_check", "gl_drivers", "gl_spinner",
     2033    "gl_colorspace_conversions", "gl_window_backing_base", "window_backend",
     2034    ):
     2035    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     2036toggle_packages(client_ENABLED and opengl_ENABLED and (gtk2_ENABLED or gtk3_ENABLED), "xpra.client.gl.gtk_base")
     2037
     2038
     2039toggle_modules(sound_ENABLED, "xpra.sound")
     2040toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11542041
    11552042toggle_packages(clipboard_ENABLED, "xpra.clipboard")
    11562043if clipboard_ENABLED:
    1157     cython_add(Extension("xpra.gtk_common.gdk_atoms",
    1158                 ["xpra/gtk_common/gdk_atoms.pyx"],
    1159                 **pkgconfig(*PYGTK_PACKAGES)
    1160                 ))
    1161 
    1162 if cyxor_ENABLED:
     2044    if PYTHON3:
     2045        cython_add(Extension("xpra.gtk_common.gtk3.gdk_atoms",
     2046                             ["xpra/gtk_common/gtk3/gdk_atoms.pyx"],
     2047                             **pkgconfig("gtk+-3.0")
     2048                             ))
     2049    else:
     2050        cython_add(Extension("xpra.gtk_common.gtk2.gdk_atoms",
     2051                             ["xpra/gtk_common/gtk2/gdk_atoms.pyx"],
     2052                             **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     2053                             ))
     2054
     2055O3_pkgconfig = pkgconfig(optimize=3)
     2056toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     2057if client_ENABLED or server_ENABLED:
    11632058    cython_add(Extension("xpra.codecs.xor.cyxor",
    11642059                ["xpra/codecs/xor/cyxor.pyx"],
    1165                 **pkgconfig()))
    1166 
    1167 if cymaths_ENABLED:
    1168     cython_add(Extension("xpra.server.stats.cymaths",
    1169                 ["xpra/server/stats/cymaths.pyx"],
    1170                 **pkgconfig()))
    1171 
    1172 
    1173 #needed for both nvenc and csc_cuda:
    1174 toggle_packages(csc_nvcuda_ENABLED or nvenc_ENABLED, "xpra.codecs.cuda_common")
    1175 
    1176 toggle_packages(csc_opencl_ENABLED, "xpra.codecs.csc_opencl")
    1177 toggle_packages(csc_nvcuda_ENABLED, "xpra.codecs.csc_nvcuda")
     2060                **O3_pkgconfig))
     2061if client_ENABLED or server_ENABLED or shadow_ENABLED:
     2062    cython_add(Extension("xpra.rectangle",
     2063                ["xpra/rectangle.pyx"],
     2064                **O3_pkgconfig))
     2065
     2066if server_ENABLED or shadow_ENABLED:
     2067    cython_add(Extension("xpra.server.cystats",
     2068                ["xpra/server/cystats.pyx"],
     2069                **O3_pkgconfig))
     2070    cython_add(Extension("xpra.server.window.motion",
     2071                ["xpra/server/window/motion.pyx"],
     2072                **O3_pkgconfig))
     2073
     2074if sd_listen_ENABLED:
     2075    sdp = pkgconfig("libsystemd")
     2076    cython_add(Extension("xpra.platform.xposix.sd_listen",
     2077                ["xpra/platform/xposix/sd_listen.pyx"],
     2078                **sdp))
     2079
     2080
    11782081toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11792082
     2083toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     2084if nvfbc_ENABLED:
     2085    nvfbc_pkgconfig = pkgconfig("nvfbc")
     2086    if WIN32:
     2087        add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     2088    platform = sys.platform.rstrip("0123456789")
     2089    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture_%s" % platform,
     2090                         ["xpra/codecs/nvfbc/fbc_capture_%s.pyx" % platform],
     2091                         language="c++",
     2092                         **nvfbc_pkgconfig))
     2093
    11802094toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
     2095toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     2096toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     2097
     2098if nvenc_ENABLED and cuda_kernels_ENABLED:
     2099    #find nvcc:
     2100    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     2101    if WIN32:
     2102        nvcc_exe = "nvcc.exe"
     2103        CUDA_DIR = os.environ.get("CUDA_DIR", "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA")
     2104        path_options = [os.path.join(CUDA_DIR, x, "bin") for x in (
     2105            "v10.1",
     2106            "v10.0",
     2107            "v9.2",
     2108            "v9.1",
     2109            "v9.0",
     2110            "v8.0",
     2111            "v7.5",
     2112            )] + path_options
     2113        #pycuda may link against curand, find it and ship it:
     2114        for p in path_options:
     2115            if os.path.exists(p):
     2116                add_data_files("", glob.glob("%s\\curand64*.dll" % p))
     2117                add_data_files("", glob.glob("%s\\cudart64*.dll" % p))
     2118                break
     2119    else:
     2120        nvcc_exe = "nvcc"
     2121        for v in ("", "-10.1", "-10.0", "-9.2", "-9.1", "-9.0", "-8.0", "-7.5"):
     2122            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     2123    options = [os.path.join(x, nvcc_exe) for x in path_options]
     2124    def which(cmd):
     2125        try:
     2126            code, out, _ = get_status_output(["which", cmd])
     2127            if code==0:
     2128                return out
     2129        except:
     2130            pass
     2131        return None
     2132    #prefer the one we find on the $PATH, if any:
     2133    try:
     2134        v = which(nvcc_exe)
     2135        if v and (v not in options):
     2136            options.insert(0, v)
     2137    except:
     2138        pass
     2139    nvcc_versions = {}
     2140    def get_nvcc_version(command):
     2141        if not os.path.exists(command):
     2142            return None
     2143        code, out, _ = get_status_output([command, "--version"])
     2144        if code!=0:
     2145            return None
     2146        vpos = out.rfind(", V")
     2147        if vpos>0:
     2148            version = out[vpos+3:].strip("\n")
     2149            version_str = " version %s" % version
     2150        else:
     2151            version = "0"
     2152            version_str = " unknown version!"
     2153        print("found CUDA compiler: %s%s" % (filename, version_str))
     2154        return tuple(int(x) for x in version.split("."))
     2155    for filename in options:
     2156        vnum = get_nvcc_version(filename)
     2157        if vnum:
     2158            nvcc_versions[vnum] = filename
     2159    assert nvcc_versions, "cannot find nvcc compiler!"
     2160    #choose the most recent one:
     2161    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     2162    if len(nvcc_versions)>1:
     2163        print(" using version %s from %s" % (version, nvcc))
     2164    if WIN32:
     2165        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     2166        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     2167    #first compile the cuda kernels
     2168    #(using the same cuda SDK for both nvenc modules for now..)
     2169    #TODO:
     2170    # * compile directly to output directory instead of using data files?
     2171    # * detect which arches we want to build for? (does it really matter much?)
     2172    kernels = ("ARGB_to_NV12", "ARGB_to_YUV444", "BGRA_to_NV12", "BGRA_to_YUV444")
     2173    for kernel in kernels:
     2174        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     2175        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     2176        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     2177            continue
     2178        reason = should_rebuild(cuda_src, cuda_bin)
     2179        if not reason:
     2180            continue
     2181        print("rebuilding %s: %s" % (kernel, reason))
     2182        cmd = [nvcc,
     2183               '-fatbin',
     2184               #"-cubin",
     2185               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     2186               #"-gencode=arch=compute_50,code=sm_50",
     2187               #"-gencode=arch=compute_52,code=sm_52",
     2188               #"-gencode=arch=compute_52,code=compute_52",
     2189               "-c", cuda_src,
     2190               "-o", cuda_bin]
     2191        #GCC 8.1 has compatibility issues with CUDA 9.2,
     2192        #so revert to C++03:
     2193        if get_gcc_version()>=[8, 1]:
     2194            cmd.append("-std=c++03")
     2195        #GCC 6 uses C++11 by default:
     2196        elif get_gcc_version()>=[6, 0]:
     2197            cmd.append("-std=c++11")
     2198        CL_VERSION = os.environ.get("CL_VERSION")
     2199        if CL_VERSION:
     2200            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     2201            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     2202            cmd += ["--machine", "32"]
     2203        if WIN32:
     2204            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     2205            #cmd += ["--input-drive-prefix", "/"]
     2206            #cmd += ["--dependency-drive-prefix", "/"]
     2207            cmd += ["-I%s" % os.path.abspath("win32")]
     2208        comp_code_options = [(30, 30), (35, 35)]
     2209        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     2210        if version!=(0,) and version<(7, 5):
     2211            print("CUDA version %s is very unlikely to work")
     2212            print("try upgrading to version 7.5 or later")
     2213        if version>=(7, 5):
     2214            comp_code_options.append((50, 50))
     2215            comp_code_options.append((52, 52))
     2216            comp_code_options.append((53, 53))
     2217        if version>=(8, 0):
     2218            comp_code_options.append((60, 60))
     2219            comp_code_options.append((61, 61))
     2220            comp_code_options.append((62, 62))
     2221        if version>=(9, 0):
     2222            comp_code_options.append((70, 70))
     2223        if version>=(10, 0):
     2224            comp_code_options.append((75, 75))
     2225        for arch, code in comp_code_options:
     2226            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     2227        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     2228        print(" %s" % " ".join("'%s'" % x for x in cmd))
     2229        c, stdout, stderr = get_status_output(cmd)
     2230        if c!=0:
     2231            print("Error: failed to compile CUDA kernel %s" % kernel)
     2232            print(stdout or "")
     2233            print(stderr or "")
     2234            sys.exit(1)
     2235    CUDA_BIN = "%s/cuda" % share_xpra
     2236    if WIN32:
     2237        CUDA_BIN = "CUDA"
     2238    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2239
    11812240if nvenc_ENABLED:
    1182     make_constants("xpra", "codecs", "nvenc", "constants")
    1183     nvenc_pkgconfig = pkgconfig("nvenc3", "cuda")
    1184     cython_add(Extension("xpra.codecs.nvenc.encoder",
    1185                          ["xpra/codecs/nvenc/encoder.pyx"],
    1186                          **nvenc_pkgconfig), min_version=(0, 16))
     2241    nvencmodule = "nvenc"
     2242    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2243    #don't link against libnvidia-encode, we load it dynamically:
     2244    libraries = nvenc_pkgconfig.get("libraries", [])
     2245    if "nvidia-encode" in libraries:
     2246        libraries.remove("nvidia-encode")
     2247    if not cython_version_compare("0.29"):
     2248        #older versions emit spurious warnings:
     2249        print("Warning: using workaround for outdated version of cython")
     2250        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-error=sign-compare")
     2251    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2252                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2253                         **nvenc_pkgconfig))
    11872254
    11882255toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892256if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2257    x264_pkgconfig = pkgconfig("x264")
     2258    if get_gcc_version()>=[6, 0]:
     2259        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912260    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922261                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2262                **x264_pkgconfig))
    11942263
    11952264toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962265if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2266    x265_pkgconfig = pkgconfig("x265")
    11982267    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992268                ["xpra/codecs/enc_x265/encoder.pyx"],
    1200                 **x265_pkgconfig), min_version=(0, 16))
     2269                **x265_pkgconfig))
     2270
     2271toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2272if pillow_ENABLED:
     2273    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
    12012274
    12022275toggle_packages(webp_ENABLED, "xpra.codecs.webp")
    12032276if webp_ENABLED:
    1204     webp_pkgconfig = pkgconfig("webp")
    1205     cython_add(Extension("xpra.codecs.webp.encode",
    1206                 ["xpra/codecs/webp/encode.pyx"],
    1207                 **webp_pkgconfig), min_version=(0, 16))
    1208 
    1209 toggle_packages(dec_avcodec_ENABLED, "xpra.codecs.dec_avcodec")
    1210 if dec_avcodec_ENABLED:
    1211     make_constants("xpra", "codecs", "dec_avcodec", "constants")
    1212     avcodec_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec_static_ENABLED)
    1213     cython_add(Extension("xpra.codecs.dec_avcodec.decoder",
    1214                 ["xpra/codecs/dec_avcodec/decoder.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1215                 **avcodec_pkgconfig), min_version=(0, 19))
     2277    webp_pkgconfig = pkgconfig("libwebp")
     2278    cython_add(Extension("xpra.codecs.webp.encoder",
     2279                    ["xpra/codecs/webp/encoder.pyx"],
     2280                    **webp_pkgconfig))
     2281    cython_add(Extension("xpra.codecs.webp.decoder",
     2282                ["xpra/codecs/webp/decoder.pyx"],
     2283                **webp_pkgconfig))
     2284
     2285jpeg = jpeg_decoder_ENABLED or jpeg_encoder_ENABLED
     2286toggle_packages(jpeg, "xpra.codecs.jpeg")
     2287if jpeg:
     2288    if jpeg_encoder_ENABLED:
     2289        jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2290        if not pkg_config_version("1.4", "libturbojpeg"):
     2291            #older versions don't have const argument:
     2292            remove_from_keywords(jpeg_pkgconfig, 'extra_compile_args', "-Werror")
     2293        cython_add(Extension("xpra.codecs.jpeg.encoder",
     2294                ["xpra/codecs/jpeg/encoder.pyx"],
     2295                **jpeg_pkgconfig))
     2296    if jpeg_decoder_ENABLED:
     2297        jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2298        cython_add(Extension("xpra.codecs.jpeg.decoder",
     2299                ["xpra/codecs/jpeg/decoder.pyx"],
     2300                **jpeg_pkgconfig))
     2301
     2302#swscale and avcodec2 use libav_common/av_log:
     2303libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2304toggle_packages(libav_common, "xpra.codecs.libav_common")
     2305if libav_common:
     2306    avutil_pkgconfig = pkgconfig("libavutil")
     2307    if get_gcc_version()>=[9, 0]:
     2308        add_to_keywords(avutil_pkgconfig, 'extra_compile_args', "-Wno-error=attributes")
     2309    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2310                ["xpra/codecs/libav_common/av_log.pyx"],
     2311                **avutil_pkgconfig))
     2312
    12162313
    12172314toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182315if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2316    avcodec2_pkgconfig = pkgconfig("libavcodec", "libavutil", "libavformat")
     2317    if get_gcc_version()>=[9, 0]:
     2318        add_to_keywords(avcodec2_pkgconfig, 'extra_compile_args', "-Wno-error=attributes")
    12202319    cython_add(Extension("xpra.codecs.dec_avcodec2.decoder",
    1221                 ["xpra/codecs/dec_avcodec2/decoder.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1222                 **avcodec2_pkgconfig), min_version=(0, 19))
    1223 
     2320                ["xpra/codecs/dec_avcodec2/decoder.pyx", "xpra/codecs/dec_avcodec2/register_compat.c"],
     2321                **avcodec2_pkgconfig))
     2322
     2323
     2324toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2325if csc_libyuv_ENABLED:
     2326    libyuv_pkgconfig = pkgconfig("libyuv")
     2327    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2328                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2329                language="c++",
     2330                **libyuv_pkgconfig))
    12242331
    12252332toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262333if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2334    swscale_pkgconfig = pkgconfig("libswscale", "libavutil")
     2335    if get_gcc_version()>=[9, 0]:
     2336        add_to_keywords(swscale_pkgconfig, 'extra_compile_args', "-Wno-error=attributes")
    12292337    cython_add(Extension("xpra.codecs.csc_swscale.colorspace_converter",
    1230                 ["xpra/codecs/csc_swscale/colorspace_converter.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1231                 **swscale_pkgconfig), min_version=(0, 19))
    1232 
    1233 toggle_packages(csc_cython_ENABLED, "xpra.codecs.csc_cython")
    1234 if csc_cython_ENABLED:
    1235     csc_cython_pkgconfig = pkgconfig()
    1236     cython_add(Extension("xpra.codecs.csc_cython.colorspace_converter",
    1237                 ["xpra/codecs/csc_cython/colorspace_converter.pyx", "xpra/codecs/memalign/memalign.c"],
    1238                 **csc_cython_pkgconfig), min_version=(0, 15))
     2338                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2339                **swscale_pkgconfig))
     2340
    12392341
    12402342toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412343if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2344    vpx_pkgconfig = pkgconfig("vpx")
    12432345    cython_add(Extension("xpra.codecs.vpx.encoder",
    1244                 ["xpra/codecs/vpx/encoder.pyx", "xpra/codecs/vpx/vpxlib.c", "xpra/codecs/memalign/memalign.c"],
    1245                 **vpx_pkgconfig), min_version=(0, 16))
     2346                ["xpra/codecs/vpx/encoder.pyx"],
     2347                **vpx_pkgconfig))
    12462348    cython_add(Extension("xpra.codecs.vpx.decoder",
    1247                 ["xpra/codecs/vpx/decoder.pyx", "xpra/codecs/memalign/memalign.c"],
    1248                 **vpx_pkgconfig), min_version=(0, 16))
    1249 
    1250 
    1251 toggle_packages(rencode_ENABLED, "xpra.net.rencode")
    1252 if rencode_ENABLED:
    1253     rencode_pkgconfig = pkgconfig()
    1254     if not debug_ENABLED:
    1255         if WIN32:
    1256             add_to_keywords(rencode_pkgconfig, 'extra_compile_args', "/Ox")
    1257         else:
    1258             add_to_keywords(rencode_pkgconfig, 'extra_compile_args', "-O3")
    1259     cython_add(Extension("xpra.net.rencode._rencode",
    1260                 ["xpra/net/rencode/rencode.pyx"],
    1261                 **rencode_pkgconfig))
     2349                ["xpra/codecs/vpx/decoder.pyx"],
     2350                **vpx_pkgconfig))
     2351
     2352toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2353if enc_ffmpeg_ENABLED:
     2354    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2355    if get_gcc_version()>=[9, 0]:
     2356        add_to_keywords(ffmpeg_pkgconfig, 'extra_compile_args', "-Wno-error=attributes")
     2357    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2358                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2359                **ffmpeg_pkgconfig))
     2360
     2361toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2362if v4l2_ENABLED:
     2363    v4l2_pkgconfig = pkgconfig()
     2364    #fuly warning: cython makes this difficult,
     2365    #we have to figure out if "device_caps" exists in the headers:
     2366    ENABLE_DEVICE_CAPS = False
     2367    if os.path.exists("/usr/include/linux/videodev2.h"):
     2368        hdata = open("/usr/include/linux/videodev2.h").read()
     2369        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2370    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2371    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2372    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2373                ["xpra/codecs/v4l2/pusher.pyx"],
     2374                **v4l2_pkgconfig))
    12622375
    12632376
    12642377toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2378toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652379if cython_bencode_ENABLED:
    1266     bencode_pkgconfig = pkgconfig()
    1267     if not debug_ENABLED:
    1268         if WIN32:
    1269             add_to_keywords(bencode_pkgconfig, 'extra_compile_args', "/Ox")
    1270         else:
    1271             add_to_keywords(bencode_pkgconfig, 'extra_compile_args', "-O3")
     2380    bencode_pkgconfig = pkgconfig(optimize=3)
    12722381    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732382                ["xpra/net/bencode/cython_bencode.pyx"],
    12742383                **bencode_pkgconfig))
    12752384
     2385if netdev_ENABLED:
     2386    netdev_pkgconfig = pkgconfig()
     2387    cython_add(Extension("xpra.platform.xposix.netdev_query",
     2388                ["xpra/platform/xposix/netdev_query.pyx"],
     2389                **netdev_pkgconfig))
     2390
     2391if vsock_ENABLED:
     2392    vsock_pkgconfig = pkgconfig()
     2393    cython_add(Extension("xpra.net.vsock",
     2394                ["xpra/net/vsock.pyx"],
     2395                **vsock_pkgconfig))
     2396
     2397if pam_ENABLED:
     2398    pam_pkgconfig = pkgconfig()
     2399    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2400    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2401    cython_add(Extension("xpra.server.pam",
     2402                ["xpra/server/pam.pyx"],
     2403                **pam_pkgconfig))
     2404
    12762405
    12772406if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2407    from Cython.Build import cythonize
     2408    #this causes Cython to fall over itself:
     2409    #gdb_debug=debug_ENABLED
     2410    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792411if cmdclass:
    12802412    setup_options["cmdclass"] = cmdclass
     
    12832415
    12842416
    1285 def print_option(prefix, k, v):
    1286     if type(v)==dict:
    1287         print("%s* %s:" % (prefix, k))
    1288         for kk,vv in v.items():
    1289             print_option(" "+prefix, kk, vv)
    1290     else:
    1291         print("%s* %s=%s" % (prefix, k, v))
    1292 
    12932417def main():
    12942418    if OSX or WIN32 or debug_ENABLED:
     2419        print()
    12952420        print("setup options:")
     2421        if verbose_ENABLED:
     2422            print("setup_options=%s" % (setup_options,))
     2423        try:
     2424            from xpra.util import repr_ellipsized as pv
     2425        except ImportError:
     2426            def pv(v):
     2427                return str(v)
    12962428        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2429            print_option("", k, pv(v))
    12982430        print("")
    12992431
Note: See TracChangeset for help on using the changeset viewer.