xpra icon
Bug tracker and wiki

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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r20517  
    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@devloop.org.uk>
    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
    1413import glob
    1514from distutils.core import setup
    1615from distutils.extension import Extension
    17 import subprocess, sys, traceback
     16import sys
    1817import os.path
    19 import stat
     18from distutils.command.build import build
     19from distutils.command.install_data import install_data
     20import shutil
     21
     22if sys.version<'2.7':
     23    raise Exception("xpra no longer supports Python 2 versions older than 2.7")
     24if sys.version[0]=='3' and sys.version<'3.4':
     25    raise Exception("xpra no longer supports Python 3 versions older than 3.4")
     26#we don't support versions of Python without the new ssl code:
     27import ssl
     28assert ssl.SSLContext, "xpra requires a Python version with ssl.SSLContext support"
    2029
    2130print(" ".join(sys.argv))
    2231
     32#*******************************************************************************
     33# build options, these may get modified further down..
     34#
    2335import xpra
    24 from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
    25 
    26 WIN32 = sys.platform.startswith("win")
     36data_files = []
     37modules = []
     38packages = []       #used by py2app
     39excludes = []       #only used by cx_freeze on win32
     40ext_modules = []
     41cmdclass = {}
     42scripts = []
     43description = "multi-platform screen and application forwarding system"
     44long_description = "Xpra is a multi platform persistent remote display server and client for " + \
     45            "forwarding applications and desktop screens. Also known as 'screen for X11'."
     46url = "http://xpra.org/"
     47
     48
     49XPRA_VERSION = xpra.__version__         #@UndefinedVariable
     50setup_options = {
     51                 "name"             : "xpra",
     52                 "version"          : XPRA_VERSION,
     53                 "license"          : "GPLv2+",
     54                 "author"           : "Antoine Martin",
     55                 "author_email"     : "antoine@devloop.org.uk",
     56                 "url"              : url,
     57                 "download_url"     : "http://xpra.org/src/",
     58                 "description"      : description,
     59                 "long_description" : long_description,
     60                 "data_files"       : data_files,
     61                 "py_modules"       : modules,
     62                 }
     63
     64WIN32 = sys.platform.startswith("win") or sys.platform.startswith("msys")
    2765OSX = sys.platform.startswith("darwin")
    28 
    29 
     66LINUX = sys.platform.startswith("linux")
     67NETBSD = sys.platform.startswith("netbsd")
     68FREEBSD = sys.platform.startswith("freebsd")
     69OPENBSD = sys.platform.startswith("openbsd")
     70
     71PYTHON3 = sys.version_info[0] == 3
     72POSIX = os.name=="posix"
     73
     74
     75if "pkg-info" in sys.argv:
     76    with open("PKG-INFO", "wb") as f:
     77        pkg_info_values = setup_options.copy()
     78        pkg_info_values.update({
     79                                "metadata_version"  : "1.1",
     80                                "summary"           :  description,
     81                                "home_page"         : url,
     82                                })
     83        for k in ("Metadata-Version", "Name", "Version", "Summary", "Home-page",
     84                  "Author", "Author-email", "License", "Download-URL", "Description"):
     85            v = pkg_info_values[k.lower().replace("-", "_")]
     86            f.write(b"%s: %s\n" % (k, v))
     87    sys.exit(0)
     88
     89
     90print("Xpra version %s" % XPRA_VERSION)
    3091#*******************************************************************************
    3192# Most of the options below can be modified on the command line
     
    3394# only the default values are specified here:
    3495#*******************************************************************************
     96from xpra.os_util import get_status_output, getUbuntuVersion, PYTHON3, BITS, \
     97    is_Ubuntu, is_Debian, is_Raspbian, is_Fedora, is_CentOS
     98
     99PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     100has_pkg_config = False
     101if PKG_CONFIG:
     102    v = get_status_output([PKG_CONFIG, "--version"])
     103    has_pkg_config = v[0]==0 and v[1]
     104    if has_pkg_config:
     105        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     106    else:
     107        print("WARNING: pkg-config not found!")
     108
     109from Cython.Compiler.Version import version as cython_version
     110
     111for arg in list(sys.argv):
     112    if arg.startswith("--pkg-config-path="):
     113        pcp = arg[len("--pkg-config-path="):]
     114        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     115        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     116        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     117        sys.argv.remove(arg)
     118
     119def no_pkgconfig(*_pkgs_options, **_ekw):
     120    return {}
     121
    35122def 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 
     123    cmd = [PKG_CONFIG]  + [str(x) for x in args]
     124    return get_status_output(cmd)[0]==0
     125
     126def pkg_config_version(req_version, pkgname):
     127    cmd = [PKG_CONFIG, "--modversion", pkgname]
     128    r, out, _ = get_status_output(cmd)
     129    if r!=0 or not out:
     130        return False
     131    from distutils.version import LooseVersion
     132    return LooseVersion(out)>=LooseVersion(req_version)
     133
     134def is_RH():
     135    try:
     136        with open("/etc/redhat-release", mode='rb') as f:
     137            data = f.read()
     138        return data.startswith("CentOS") or data.startswith("RedHat")
     139    except:
     140        pass
     141    return False
     142
     143DEFAULT = True
     144if "--minimal" in sys.argv:
     145    sys.argv.remove("--minimal")
     146    DEFAULT = False
     147
     148from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     149shadow_ENABLED = SHADOW_SUPPORTED and DEFAULT
     150server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and DEFAULT
     151rfb_ENABLED = server_ENABLED
     152service_ENABLED = LINUX and server_ENABLED
     153sd_listen_ENABLED = POSIX and pkg_config_ok("--exists", "libsystemd") and (not is_Ubuntu() or getUbuntuVersion()>[16, 4])
     154proxy_ENABLED  = DEFAULT
     155client_ENABLED = DEFAULT
     156scripts_ENABLED = not WIN32
     157
     158x11_ENABLED = DEFAULT and not WIN32 and not OSX
     159xinput_ENABLED = x11_ENABLED
     160uinput_ENABLED = x11_ENABLED
     161dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     162gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     163gtk2_ENABLED = DEFAULT and client_ENABLED and not PYTHON3
     164gtk3_ENABLED = DEFAULT and client_ENABLED and PYTHON3
     165opengl_ENABLED = DEFAULT and client_ENABLED
     166html5_ENABLED = DEFAULT
     167html5_gzip_ENABLED = DEFAULT
     168html5_brotli_ENABLED = DEFAULT
     169minify_ENABLED = html5_ENABLED
     170pam_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"))
     171
     172xdg_open_ENABLED        = LINUX and DEFAULT
     173netdev_ENABLED          = LINUX and DEFAULT
     174vsock_ENABLED           = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h")
     175bencode_ENABLED         = DEFAULT
     176cython_bencode_ENABLED  = DEFAULT
     177clipboard_ENABLED       = DEFAULT
     178Xdummy_ENABLED          = None          #None means auto-detect
     179Xdummy_wrapper_ENABLED  = None          #None means auto-detect
     180if WIN32 or OSX:
     181    Xdummy_ENABLED = False
     182sound_ENABLED           = DEFAULT
     183printing_ENABLED        = DEFAULT
     184crypto_ENABLED          = DEFAULT
     185mdns_ENABLED            = DEFAULT
     186
     187enc_proxy_ENABLED       = DEFAULT
     188enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264")
     189enc_x265_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x265")
     190pillow_ENABLED          = DEFAULT
     191webp_ENABLED            = DEFAULT and pkg_config_version("0.5", "libwebp")
     192jpeg_ENABLED            = DEFAULT and pkg_config_version("1.2", "libturbojpeg")
     193vpx_ENABLED             = DEFAULT and pkg_config_version("1.4", "vpx")
     194enc_ffmpeg_ENABLED      = 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        = True
    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         = True
    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",
     222SWITCHES = ["enc_x264", "enc_x265", "enc_ffmpeg",
     223            "nvenc", "cuda_kernels", "cuda_rebuild", "nvfbc",
     224            "vpx", "webp", "pillow", "jpeg",
     225            "v4l2",
     226            "dec_avcodec2", "csc_swscale",
     227            "csc_libyuv",
     228            "bencode", "cython_bencode", "vsock", "netdev", "mdns",
    104229            "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")
     230            "scripts",
     231            "server", "client", "dbus", "x11", "xinput", "uinput", "sd_listen",
     232            "gtk_x11", "service",
     233            "gtk2", "gtk3", "example",
     234            "html5", "minify", "html5_gzip", "html5_brotli",
     235            "pam", "xdg_open",
     236            "sound", "opengl", "printing", "webcam", "notifications", "keyboard",
     237            "rebuild",
     238            "annotate", "warn", "strict",
     239            "shadow", "proxy", "rfb",
     240            "debug", "PIC",
     241            "Xdummy", "Xdummy_wrapper", "verbose", "tests", "bundle_tests"]
     242if WIN32:
     243    SWITCHES.append("zip")
     244    zip_ENABLED = True
    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 = "dist"
     263rpath = None
     264ssl_cert = None
     265ssl_key = None
     266minifier = None
     267if WIN32:
     268    share_xpra = ""
     269else:
     270    share_xpra = "share/xpra/"
    124271filtered_args = []
    125272for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     273    matched = False
     274    for x in ("rpath", "ssl-cert", "ssl-key", "install", "share-xpra"):
     275        varg = "--%s=" % x
     276        if arg.startswith(varg):
     277            value = arg[len(varg):]
     278            globals()[x.replace("-", "_")] = value
     279            #remove these arguments from sys.argv,
     280            #except for --install=PATH
     281            matched = x!="install"
     282            break
     283    if matched:
    129284        continue
    130     matched = False
    131285    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     286        with_str = "--with-%s" % x
     287        without_str = "--without-%s" % x
     288        if arg.startswith(with_str+"="):
     289            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     290            matched = True
     291            break
     292        elif arg==with_str:
    133293            vars()["%s_ENABLED" % x] = True
    134294            matched = True
    135295            break
    136         elif arg=="--without-%s" % x:
     296        elif arg==without_str:
    137297            vars()["%s_ENABLED" % x] = False
    138298            matched = True
     
    145305    for x in SWITCHES:
    146306        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)
     307    print("build switches:")
     308    for k in sorted(SWITCHES):
     309        v = switches_info[k]
     310        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
    150311
    151312    #sanity check the flags:
     
    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)
     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 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")
    174343
    175344
    176345#*******************************************************************************
    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",
     346# default sets:
     347
     348external_includes = ["hashlib",
    204349                     "ctypes", "platform"]
     350
     351
     352if gtk3_ENABLED or (sound_ENABLED and PYTHON3):
     353    external_includes += ["gi"]
     354elif gtk2_ENABLED or x11_ENABLED:
     355    external_includes += "cairo", "pango", "pangocairo", "atk", "glib", "gobject", "gio", "gtk.keysyms"
    205356
    206357external_excludes = [
     
    212363                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213364                    #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"
     365                    "curses", "pdb",
     366                    "tty",
     367                    "setuptools", "doctest"
    219368                    ]
    220 
     369if not html5_ENABLED and not crypto_ENABLED:
     370    external_excludes += ["ssl", "_ssl"]
     371if not html5_ENABLED:
     372    external_excludes += ["BaseHTTPServer", "mimetypes", "mimetools"]
     373
     374if not client_ENABLED and not server_ENABLED:
     375    excludes += ["PIL"]
     376if not dbus_ENABLED:
     377    excludes += ["dbus"]
    221378
    222379
    223380#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     381#for distutils / py2app and cx_freeze
    225382#use the following functions, which should get the right
    226383#data in the global variables "packages", "modules" and "excludes"
     
    253410
    254411def add_modules(*mods):
     412    def add(v):
     413        global modules
     414        if v not in modules:
     415            modules.append(v)
     416    do_add_modules(add, *mods)
     417
     418def do_add_modules(op, *mods):
    255419    """ adds the packages and any .py module found in the packages to the "modules" list
    256420    """
    257421    global modules
    258422    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     423        #ugly path stripping:
     424        if x.startswith("./"):
     425            x = x[2:]
     426        if x.endswith(".py"):
     427            x = x[:-3]
     428            x = x.replace("/", ".") #.replace("\\", ".")
    261429        pathname = os.path.sep.join(x.split("."))
     430        #is this a file module?
     431        f = "%s.py" % pathname
     432        if os.path.exists(f) and os.path.isfile(f):
     433            op(x)
    262434        if os.path.exists(pathname) and os.path.isdir(pathname):
    263435            #add all file modules found in this directory
    264436            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     437                #make sure we only include python files,
     438                #and ignore eclipse copies
     439                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266440                    fname = os.path.join(pathname, f)
    267441                    if os.path.isfile(fname):
    268442                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     443                        op(modname)
    270444
    271445def toggle_packages(enabled, *module_names):
     
    275449        remove_packages(*module_names)
    276450
     451def toggle_modules(enabled, *module_names):
     452    if enabled:
     453        def op(v):
     454            global modules
     455            if v not in modules:
     456                modules.append(v)
     457        do_add_modules(op, *module_names)
     458    else:
     459        remove_packages(*module_names)
     460
     461
    277462#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 
     463add_modules("xpra", "xpra.platform", "xpra.net")
     464add_modules("xpra.scripts.main")
     465
     466
     467def add_data_files(target_dir, files):
     468    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     469    assert type(target_dir)==str
     470    assert type(files) in (list, tuple)
     471    data_files.append((target_dir, files))
     472
     473
     474#for pretty printing of options:
     475def print_option(prefix, k, v):
     476    if type(v)==dict:
     477        print("%s* %s:" % (prefix, k))
     478        for kk,vv in v.items():
     479            print_option(" "+prefix, kk, vv)
     480    else:
     481        print("%s* %s=%s" % (prefix, k, v))
    284482
    285483#*******************************************************************************
    286484# Utility methods for building with Cython
    287485def cython_version_check(min_version):
    288     try:
    289         from Cython.Compiler.Version import version as cython_version
    290     except ImportError, e:
    291         sys.exit("ERROR: Cannot find Cython: %s" % e)
    292486    from distutils.version import LooseVersion
    293487    if LooseVersion(cython_version) < LooseVersion(".".join([str(x) for x in min_version])):
     
    297491                 % (cython_version, ".".join([str(part) for part in min_version])))
    298492
    299 def cython_add(extension, min_version=(0, 14, 0)):
     493def cython_add(extension, min_version=(0, 19)):
    300494    #gentoo does weird things, calls --no-compile with build *and* install
    301495    #then expects to find the cython modules!? ie:
     
    303497    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304498        return
    305     global ext_modules, cmdclass
    306499    cython_version_check(min_version)
    307500    from Cython.Distutils import build_ext
    308501    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     502    global cmdclass
     503    cmdclass['build_ext'] = build_ext
     504
     505def insert_into_keywords(kw, key, *args):
     506    values = kw.setdefault(key, [])
     507    for arg in args:
     508        values.insert(0, arg)
    310509
    311510def add_to_keywords(kw, key, *args):
     
    319518
    320519
     520def checkdirs(*dirs):
     521    for d in dirs:
     522        if not os.path.exists(d) or not os.path.isdir(d):
     523            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     524
    321525PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
     526#override the pkgconfig file,
     527#we don't need to link against any of these:
     528gtk2_ignored_tokens=[("-l%s" % x) for x in
     529                     ["fontconfig", "freetype", "cairo",
     530                      "atk-1.0", "pangoft2-1.0", "pango-1.0", "pangocairo-1.0",
     531                      "gio-2.0", "gdk_pixbuf-2.0"]]
    322532
    323533GCC_VERSION = []
     
    326536    if len(GCC_VERSION)==0:
    327537        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:
     538        r, _, err = get_status_output(cmd)
     539        if r==0:
    332540            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     541            for line in err.splitlines():
    334542                if line.startswith(V_LINE):
    335543                    v_str = line[len(V_LINE):].split(" ")[0]
     
    343551    return GCC_VERSION
    344552
    345 def make_constants_pxi(constants_path, pxi_path):
     553def make_constants_pxi(constants_path, pxi_path, **kwargs):
    346554    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):
     555    with open(constants_path) as f:
     556        for line in f:
     557            data = line.split("#", 1)[0].strip()
     558            # data can be empty ''...
     559            if not data:
     560                continue
     561            # or a pair like 'cFoo "Foo"'...
     562            elif len(data.split()) == 2:
     563                (pyname, cname) = data.split()
     564                constants.append((pyname, cname))
     565            # or just a simple token 'Foo'
     566            else:
     567                constants.append(data)
     568
     569    with open(pxi_path, "w") as out:
     570        if constants:
     571            out.write("cdef extern from *:\n")
     572            ### Apparently you can't use | on enum's?!
     573            # out.write("    enum MagicNumbers:\n")
     574            # for const in constants:
     575            #     if isinstance(const, tuple):
     576            #         out.write('        %s %s\n' % const)
     577            #     else:
     578            #         out.write('        %s\n' % (const,))
     579            for const in constants:
     580                if isinstance(const, tuple):
     581                    out.write('    unsigned int %s %s\n' % const)
     582                else:
     583                    out.write('    unsigned int %s\n' % (const,))
     584
     585            out.write("constants = {\n")
     586            for const in constants:
     587                if isinstance(const, tuple):
     588                    pyname = const[0]
     589                else:
     590                    pyname = const
     591                out.write('    "%s": %s,\n' % (pyname, pyname))
     592            out.write("}\n")
     593            if kwargs:
     594                out.write("\n\n")
     595
     596        if kwargs:
     597            for k, v in kwargs.items():
     598                out.write('DEF %s = %s\n' % (k, v))
     599
     600
     601def should_rebuild(src_file, bin_file):
     602    if not os.path.exists(bin_file):
     603        return "no file"
     604    elif rebuild_ENABLED:
     605        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     606            return "binary file out of date"
     607        elif os.path.getctime(bin_file)<os.path.getctime(__file__):
     608            return "newer build file"
     609    return None
     610
     611def make_constants(*paths, **kwargs):
    384612    base = os.path.join(os.getcwd(), *paths)
    385613    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"
     614    try:
     615        pxi_file = kwargs.pop("pxi_file")
     616    except KeyError:
     617        pxi_file = "%s.pxi" % base
     618    reason = should_rebuild(constants_file, pxi_file)
    394619    if reason:
    395620        if verbose_ENABLED:
    396621            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
     622        make_constants_pxi(constants_file, pxi_file, **kwargs)
     623
    414624
    415625# 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 
     626def exec_pkgconfig(*pkgs_options, **ekw):
    423627    kw = dict(ekw)
     628    optimize = kw.pop("optimize", None)
     629    if optimize and not debug_ENABLED:
     630        if type(optimize)==bool:
     631            optimize = int(optimize)*3
     632        add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     633    ignored_flags = kw.pop("ignored_flags", [])
     634    ignored_tokens = kw.pop("ignored_tokens", [])
     635
     636    #for distros that don't patch distutils,
     637    #we have to add the python cflags:
     638    if not (is_Fedora() or is_Debian() or is_CentOS()):
     639        import shlex
     640        import sysconfig
     641        for x in shlex.split(sysconfig.get_config_var('CFLAGS') or ''):
     642            add_to_keywords(kw, 'extra_compile_args', x)
     643
     644    def add_tokens(s, extra="extra_link_args", extra_map={"-W" : "extra_compile_args"}):
     645        if not s:
     646            return
     647        flag_map = {'-I': 'include_dirs',
     648                    '-L': 'library_dirs',
     649                    '-l': 'libraries'}
     650        for token in s.split():
     651            if token in ignored_tokens:
     652                pass
     653            elif token[:2] in ignored_flags:
     654                pass
     655            elif token[:2] in flag_map:
     656                if len(token)>2:
     657                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     658                else:
     659                    print("Warning: invalid token '%s'" % token)
     660            else:
     661                extra_name = extra_map.get(token, extra)
     662                add_to_keywords(kw, extra_name, token)
     663
    424664    if len(pkgs_options)>0:
    425665        package_names = []
     
    438678            for option in options:
    439679                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:
     680                r, _, _ = get_status_output(cmd)
     681                if r==0:
    443682                    valid_option = option
    444683                    break
    445684            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     685                raise Exception("ERROR: cannot find a valid pkg-config entry for %s using PKG_CONFIG_PATH=%s" % (" or ".join(options), os.environ.get("PKG_CONFIG_PATH", "(empty)")))
    447686            package_names.append(valid_option)
    448687        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))
     688            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     689        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     690        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     691        if r!=0:
     692            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     693        add_tokens(pkg_config_out)
    468694    if warn_ENABLED:
    469695        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470696        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471697    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"
     698        if os.environ.get("CC", "").find("clang")>=0:
     699            #clang emits too many warnings with cython code,
     700            #so we can't enable Werror without turning off some warnings:
     701            #this list of flags should allow clang to build the whole source tree,
     702            #as of Cython 0.26 + clang 4.0. Other version combinations may require
     703            #(un)commenting other switches.
     704            eifd = ["-Werror",
     705                    #"-Wno-unneeded-internal-declaration",
     706                    #"-Wno-unknown-attributes",
     707                    #"-Wno-unused-function",
     708                    #"-Wno-self-assign",
     709                    #"-Wno-sometimes-uninitialized",
     710                    #cython adds rpath to the compilation command??
     711                    #and the "-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1" is also ignored by clang:
     712                    "-Wno-deprecated-register",
     713                    "-Wno-unused-command-line-argument",
     714                    ]
     715        elif get_gcc_version()>=[4, 4]:
     716            eifd = ["-Werror"]
     717            if is_Debian() or is_Ubuntu() or is_Raspbian():
     718                #needed on Debian and Ubuntu to avoid this error:
     719                #/usr/include/gtk-2.0/gtk/gtkitemfactory.h:47:1: error: function declaration isn't a prototype [-Werror=strict-prototypes]
     720                eifd.append("-Wno-error=strict-prototypes")
     721            if NETBSD:
     722                #see: http://trac.cython.org/ticket/395
     723                eifd += ["-fno-strict-aliasing"]
     724            elif FREEBSD:
     725                eifd += ["-Wno-error=unused-function"]
    475726        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     727            #older versions of OSX ship an old gcc,
     728            #not much we can do with this:
     729            eifd = []
     730        for eif in eifd:
     731            add_to_keywords(kw, 'extra_compile_args', eif)
     732    if sys.version_info>=(3,7):
     733        #we'll switch to the "new" buffer interface after we drop support for Python 2.7
     734        #until then, silence those deprecation warnings:
     735        add_to_keywords(kw, 'extra_compile_args', "-Wno-error=deprecated-declarations")
    478736    if PIC_ENABLED:
    479737        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481739        add_to_keywords(kw, 'extra_compile_args', '-g')
    482740        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     741        if get_gcc_version()>=[4, 8]:
    485742            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486743            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     744    if rpath and kw.get("libraries"):
     745        insert_into_keywords(kw, "library_dirs", rpath)
     746        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
     747    add_tokens(os.environ.get("CFLAGS"), "extra_compile_args", {})
     748    add_tokens(os.environ.get("LDFLAGS"), "extra_link_args", {})
    487749    #add_to_keywords(kw, 'include_dirs', '.')
    488750    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     751        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490752    return kw
     753pkgconfig = exec_pkgconfig
    491754
    492755
    493756#*******************************************************************************
    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()
     757
     758
     759def get_base_conf_dir(install_dir, stripbuildroot=True):
     760    #in some cases we want to strip the buildroot (to generate paths in the config file)
     761    #but in other cases we want the buildroot path (when writing out the config files)
     762    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     763    #this is a bit hackish, but I can't think of a better way of detecting it
     764    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     765    dirs = (install_dir or sys.prefix).split(os.path.sep)
     766    if install_dir and stripbuildroot:
     767        pkgdir = os.environ.get("pkgdir")
     768        if "debian" in dirs and "tmp" in dirs:
     769            #ugly fix for stripping the debian tmp dir:
     770            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     771            while "tmp" in dirs:
     772                dirs = dirs[dirs.index("tmp")+1:]
     773        elif "debian" in dirs:
     774            #same for recent debian versions:
     775            #ie: "xpra-2.0.2/debian/xpra/usr" -> "usr"
     776            i = dirs.index("debian")
     777            if dirs[i+1] == "xpra":
     778                dirs = dirs[i+2:]
     779        elif "BUILDROOT" in dirs:
     780            #strip rpm style build root:
     781            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     782            dirs = dirs[dirs.index("BUILDROOT")+2:]
     783        elif pkgdir and install_dir.startswith(pkgdir):
     784            #arch build dir:
     785            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     786        elif "usr" in dirs:
     787            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     788            #assume "/usr" or "/usr/local" is the build root
     789            while "usr" in dirs[1:]:
     790                dirs = dirs[dirs[1:].index("usr")+1:]
     791        elif "image" in dirs:
     792            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     793            while "image" in dirs:
     794                dirs = dirs[dirs.index("image")+2:]
     795    #now deal with the fact that "/etc" is used for the "/usr" prefix
     796    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     797    if dirs and dirs[-1]=="usr":
     798        dirs = dirs[:-1]
     799    #is this an absolute path?
     800    if len(dirs)==0 or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     801        #ie: ["/", "usr"] or ["/", "usr", "local"]
     802        dirs.insert(0, os.path.sep)
     803    return dirs
     804
     805def get_conf_dir(install_dir, stripbuildroot=True):
     806    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     807    dirs.append("etc")
     808    dirs.append("xpra")
     809    return os.path.join(*dirs)
     810
     811def detect_xorg_setup(install_dir=None):
     812    from xpra.scripts import config
     813    config.debug = config.warn
     814    conf_dir = get_conf_dir(install_dir)
     815    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     816
     817def build_xpra_conf(install_dir):
     818    #generates an actual config file from the template
     819    xvfb_command = detect_xorg_setup(install_dir)
     820    from xpra.platform.features import DEFAULT_ENV
     821    def bstr(b):
     822        if b is None:
     823            return "auto"
     824        return ["no", "yes"][int(b)]
     825    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     826    conf_dir = get_conf_dir(install_dir)
     827    from xpra.platform.features import DEFAULT_SSH_COMMAND, DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     828    from xpra.platform.paths import get_socket_dirs
     829    from xpra.scripts.config import get_default_key_shortcuts, get_default_systemd_run, get_default_pulseaudio_command, DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO
     830    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     831    socket_dirs = get_socket_dirs()
     832    if WIN32:
     833        bind = "Main"
    533834    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()
     835        if os.getuid()>0:
     836            #remove any paths containing the uid,
     837            #osx uses /var/tmp/$UID-Xpra,
     838            #but this should not be included in the default config for all users!
     839            #(the buildbot's uid!)
     840            socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     841        bind = "auto"
     842    #FIXME: we should probably get these values from the default config instead
     843    pdf, postscript = "", ""
     844    if POSIX and printing_ENABLED:
     845        try:
     846            if "/usr/sbin" not in sys.path:
     847                sys.path.append("/usr/sbin")
     848            from xpra.platform.pycups_printing import get_printer_definition
     849            print("probing cups printer definitions")
     850            pdf = get_printer_definition("pdf")
     851            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     852            print("pdf=%s, postscript=%s" % (pdf, postscript))
     853        except Exception as e:
     854            print("could not probe for pdf/postscript printers: %s" % e)
     855    def pretty_cmd(cmd):
     856        return " ".join(cmd)
     857    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     858    #Ubuntu 16.10 has opencv builds that conflict with our private ffmpeg
     859    webcam = webcam_ENABLED and not (OSX or getUbuntuVersion()==[16, 10])
     860    #no python-avahi on RH / CentOS, need dbus module on *nix:
     861    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     862    SUBS = {
     863            'xvfb_command'          : pretty_cmd(xvfb_command),
     864            'ssh_command'           : DEFAULT_SSH_COMMAND,
     865            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     866            'remote_logging'        : "both",
     867            'start_env'             : start_env,
     868            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     869            'pulseaudio_command'    : pretty_cmd(get_default_pulseaudio_command()),
     870            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     871            'conf_dir'              : conf_dir,
     872            'bind'                  : bind,
     873            'ssl_cert'              : ssl_cert or "",
     874            'ssl_key'               : ssl_key or "",
     875            'systemd_run'           : get_default_systemd_run(),
     876            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     877            'log_dir'               : "auto",
     878            'mdns'                  : bstr(mdns),
     879            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     880            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     881            'pdf_printer'           : pdf,
     882            'postscript_printer'    : postscript,
     883            'webcam'                : ["no", "auto"][webcam],
     884            'mousewheel'            : "on",
     885            'printing'              : bstr(printing_ENABLED),
     886            'dbus_control'          : bstr(dbus_ENABLED),
     887            'mmap'                  : bstr(True),
     888            }
     889    def convert_templates(subdirs=[]):
     890        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     891        #get conf dir for install, without stripping the build root
     892        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     893        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     894        if not os.path.exists(target_dir):
     895            try:
     896                os.makedirs(target_dir)
     897            except Exception as e:
     898                print("cannot create target dir '%s': %s" % (target_dir, e))
     899        for f in sorted(os.listdir(dirname)):
     900            if f.endswith("osx.conf.in") and not OSX:
     901                continue
     902            filename = os.path.join(dirname, f)
     903            if os.path.isdir(filename):
     904                convert_templates(subdirs+[f])
     905                continue
     906            if not f.endswith(".in"):
     907                continue
     908            with open(filename, "r") as f_in:
     909                template  = f_in.read()
     910            target_file = os.path.join(target_dir, f[:-len(".in")])
     911            print("generating %s from %s" % (target_file, f))
     912            with open(target_file, "w") as f_out:
     913                config_data = template % SUBS
     914                f_out.write(config_data)
     915    convert_templates()
    576916
    577917
     
    580920    #clean and sdist don't actually use cython,
    581921    #so skip this (and avoid errors)
    582     def pkgconfig(*pkgs_options, **ekw):
    583         return {}
     922    pkgconfig = no_pkgconfig
    584923    #always include everything in this case:
    585924    add_packages("xpra")
    586925    #ensure we remove the files we generate:
    587926    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",
     927                   "xpra/build_info.py",
     928                   "xpra/monotonic_time.c",
     929                   "xpra/gtk_common/gtk2/gdk_atoms.c",
     930                   "xpra/gtk_common/gtk2/gdk_bindings.c",
     931                   "xpra/gtk_common/gtk3/gdk_atoms.c",
     932                   "xpra/gtk_common/gtk3/gdk_bindings.c",
     933                   "xpra/x11/gtk2/constants.pxi",
     934                   "xpra/x11/gtk2/gdk_bindings.c",
     935                   "xpra/x11/gtk2/gdk_display_source.c",
     936                   "xpra/x11/gtk3/constants.pxi",
     937                   "xpra/x11/gtk3/gdk_bindings.c",
     938                   "xpra/x11/gtk3/gdk_display_source.c",
    592939                   "xpra/x11/bindings/constants.pxi",
    593940                   "xpra/x11/bindings/wait_for_x_server.c",
     
    597944                   "xpra/x11/bindings/randr_bindings.c",
    598945                   "xpra/x11/bindings/core_bindings.c",
     946                   "xpra/x11/bindings/posix_display_source.c",
    599947                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     948                   "xpra/x11/bindings/xi2_bindings.c",
     949                   "xpra/platform/win32/propsys.cpp",
     950                   "xpra/platform/darwin/gdk_bindings.c",
     951                   "xpra/platform/xposix/sd_listen.c",
     952                   "xpra/platform/xposix/netdev_query.c",
     953                   "xpra/net/bencode/cython_bencode.c",
     954                   "xpra/net/vsock.c",
     955                   "xpra/buffers/membuf.c",
    601956                   "xpra/codecs/vpx/encoder.c",
    602957                   "xpra/codecs/vpx/decoder.c",
    603958                   "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     959                   "xpra/codecs/nvfbc/fbc_capture_linux.cpp",
     960                   "xpra/codecs/nvfbc/fbc_capture_win.cpp",
    605961                   "xpra/codecs/enc_x264/encoder.c",
    606962                   "xpra/codecs/enc_x265/encoder.c",
     963                   "xpra/codecs/jpeg/encoder.c",
     964                   "xpra/codecs/jpeg/decoder.c",
     965                   "xpra/codecs/enc_ffmpeg/encoder.c",
     966                   "xpra/codecs/v4l2/constants.pxi",
     967                   "xpra/codecs/v4l2/pusher.c",
     968                   "xpra/codecs/libav_common/av_log.c",
    607969                   "xpra/codecs/webp/encode.c",
    608                    "xpra/codecs/dec_avcodec/decoder.c",
    609                    "xpra/codecs/dec_avcodec/constants.pxi",
     970                   "xpra/codecs/webp/decode.c",
    610971                   "xpra/codecs/dec_avcodec2/decoder.c",
     972                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611973                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613                    "xpra/codecs/csc_cython/colorspace_converter.c",
    614974                   "xpra/codecs/xor/cyxor.c",
    615975                   "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")
     976                   "xpra/codecs/nvapi_version.c",
     977                   "xpra/gtk_common/gdk_atoms.c",
     978                   "xpra/client/gtk3/cairo_workaround.c",
     979                   "xpra/server/cystats.c",
     980                   "xpra/server/window/region.c",
     981                   "xpra/server/window/motion.c",
     982                   "xpra/server/pam.c",
     983                   "etc/xpra/xpra.conf",
     984                   #special case for the generated xpra conf files in build (see #891):
     985                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     986    if cuda_rebuild_ENABLED:
     987        CLEAN_FILES += [
     988            "xpra/codecs/cuda_common/ARGB_to_NV12.fatbin",
     989            "xpra/codecs/cuda_common/ARGB_to_YUV444.fatbin",
     990            "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     991            "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
     992            ]
     993    for x in CLEAN_FILES:
     994        p, ext = os.path.splitext(x)
     995        if ext in (".c", ".cpp", ".pxi"):
     996            #clean the Cython annotated html files:
     997            CLEAN_FILES.append(p+".html")
     998            if WIN32 and ext!=".pxi":
     999                #on win32, the build creates ".pyd" files, clean those too:
     1000                CLEAN_FILES.append(p+".pyd")
     1001                #when building with python3, we need to clean files named like:
     1002                #"xpra/codecs/csc_libyuv/colorspace_converter-cpython-36m.dll"
     1003                filename = os.path.join(os.getcwd(), p.replace("/", os.path.sep)+"*.dll")
     1004                CLEAN_FILES += glob.glob(filename)
    6231005    if 'clean' in sys.argv:
    6241006        CLEAN_FILES.append("xpra/build_info.py")
     
    6301012            os.unlink(filename)
    6311013
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     1014from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    6331015
    6341016if "clean" not in sys.argv:
    6351017    # Add build info to build_info.py file:
    6361018    record_build_info()
     1019    # ensure it is included in the module list if it didn't exist before
     1020    add_modules(BUILD_INFO_FILE)
    6371021
    6381022if "sdist" in sys.argv:
    6391023    record_src_info()
    6401024
    641 if "install" in sys.argv:
     1025if "install" in sys.argv or "build" in sys.argv:
    6421026    #if installing from source tree rather than
    6431027    #from a source snapshot, we may not have a "src_info" file
     
    6451029    if not has_src_info():
    6461030        record_src_info()
     1031        # ensure it is now included in the module list
     1032        add_modules(SRC_INFO_FILE)
    6471033
    6481034
     
    6631049    return m
    6641050
     1051
     1052def install_html5(install_dir="www"):
     1053    from setup_html5 import install_html5 as do_install_html5
     1054    do_install_html5(install_dir, minifier, html5_gzip_ENABLED, html5_brotli_ENABLED, verbose_ENABLED)
     1055
     1056
    6651057#*******************************************************************************
    6661058if 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"]
     1059    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1060    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
    8821061    add_packages("xpra.platform.win32")
    8831062    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"]
     1063
     1064    #this is where the win32 gi installer will put things:
     1065    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1066
     1067    #only add the cx_freeze specific options
     1068    #only if we are packaging:
     1069    if "install_exe" in sys.argv:
     1070        #with cx_freeze, we don't use py_modules
     1071        del setup_options["py_modules"]
     1072        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1073        if not hasattr(sys, "base_prefix"):
     1074            #workaround for broken sqlite hook with python 2.7, see:
     1075            #https://github.com/anthony-tuininga/cx_Freeze/pull/272
     1076            sys.base_prefix = sys.prefix
     1077
     1078        #cx_freeze doesn't use "data_files"...
     1079        del setup_options["data_files"]
     1080        #it wants source files first, then where they are placed...
     1081        #one item at a time (no lists)
     1082        #all in its own structure called "include_files" instead of "data_files"...
     1083        def add_data_files(target_dir, files):
     1084            if verbose_ENABLED:
     1085                print("add_data_files(%s, %s)" % (target_dir, files))
     1086            assert type(target_dir)==str
     1087            assert type(files) in (list, tuple)
     1088            for f in files:
     1089                target_file = os.path.join(target_dir, os.path.basename(f))
     1090                data_files.append((f, target_file))
     1091
     1092        #pass a potentially nested dictionary representing the tree
     1093        #of files and directories we do want to include
     1094        #relative to gnome_include_path
     1095        def add_dir(base, defs):
     1096            if verbose_ENABLED:
     1097                print("add_dir(%s, %s)" % (base, defs))
     1098            if type(defs) in (list, tuple):
     1099                for sub in defs:
     1100                    if type(sub)==dict:
     1101                        add_dir(base, sub)
     1102                    else:
     1103                        assert type(sub)==str
     1104                        filename = os.path.join(gnome_include_path, base, sub)
     1105                        if os.path.exists(filename):
     1106                            add_data_files(base, [filename])
     1107                        else:
     1108                            print("Warning: missing '%s'" % filename)
     1109            else:
     1110                assert type(defs)==dict
     1111                for d, sub in defs.items():
     1112                    assert type(sub) in (dict, list, tuple)
     1113                    #recurse down:
     1114                    add_dir(os.path.join(base, d), sub)
     1115
     1116        #convenience method for adding GI libs and "typelib" and "gir":
     1117        def add_gi(*libs):
     1118            if verbose_ENABLED:
     1119                print("add_gi(%s)" % str(libs))
     1120            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1121            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1122
     1123        def add_DLLs(*dll_names):
     1124            try:
     1125                do_add_DLLs(*dll_names)
     1126            except Exception as e:
     1127                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1128                print(" %s" % e)
     1129                sys.exit(1)
     1130
     1131        def do_add_DLLs(*dll_names):
     1132            dll_names = list(dll_names)
     1133            dll_files = []
     1134            import re
     1135            version_re = re.compile("\-[0-9\.\-]+$")
     1136            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1137            if os.path.exists(gnome_include_path):
     1138                dirs.insert(0, gnome_include_path)
     1139            if verbose_ENABLED:
     1140                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1141            for d in dirs:
     1142                if not os.path.exists(d):
     1143                    continue
     1144                for x in os.listdir(d):
     1145                    dll_path = os.path.join(d, x)
     1146                    x = x.lower()
     1147                    if os.path.isdir(dll_path) or not x.startswith("lib") or not x.endswith(".dll"):
     1148                        continue
     1149                    nameversion = x[3:-4]                       #strip "lib" and ".dll": "libatk-1.0-0.dll" -> "atk-1.0-0"
     1150                    if verbose_ENABLED:
     1151                        print("checking %s: %s" % (x, nameversion))
     1152                    m = version_re.search(nameversion)          #look for version part of filename
     1153                    if m:
     1154                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1155                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1156                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1157                    else:
     1158                        dll_version = ""                        #no version
     1159                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1160                    if dll_name in dll_names:
     1161                        #this DLL is on our list
     1162                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1163                        dll_files.append(dll_path)
     1164                        dll_names.remove(dll_name)
     1165            if len(dll_names)>0:
     1166                print("some DLLs could not be found:")
     1167                for x in dll_names:
     1168                    print(" - lib%s*.dll" % x)
     1169            add_data_files("", dll_files)
     1170
     1171        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1172        #(ie: "libatk-1.0-0.dll" -> "atk")
     1173        if sound_ENABLED or gtk3_ENABLED:
     1174            add_DLLs('gio', 'girepository', 'glib',
     1175                     'gnutls', 'gobject', 'gthread',
     1176                     'orc', 'stdc++',
     1177                     'winpthread',
     1178                     )
     1179        if gtk3_ENABLED:
     1180            add_DLLs('atk',
     1181                     'dbus', 'dbus-glib',
     1182                     'gdk', 'gdk_pixbuf', 'gtk',
     1183                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1184                     'harfbuzz', 'harfbuzz-gobject',
     1185                     'jasper', 'epoxy',
     1186                     'intl',
     1187                     'p11-kit',
     1188                     'jpeg', 'png16', 'rsvg', 'webp', 'tiff')
     1189            #these are missing in newer aio installers (sigh):
     1190            do_add_DLLs('javascriptcoregtk')
     1191            if opengl_ENABLED:
     1192                do_add_DLLs('gdkglext', 'gtkglext')
     1193
     1194        if gtk3_ENABLED:
     1195            add_dir('etc', ["fonts", "gtk-3.0", "pango", "pkcs11"])     #add "dbus-1"?
     1196            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1197                            "libvisual-0.4", "p11-kit", "pkcs11"])
     1198            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1199                              "p11-kit", "xml",
     1200                              {"icons"  : ["hicolor"]},
     1201                              {"locale" : ["en"]},
     1202                              {"themes" : ["Default"]}
     1203                             ])
     1204        if gtk3_ENABLED or sound_ENABLED:
     1205            add_dir('lib', ["gio"])
     1206            packages.append("gi")
     1207            add_gi("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1208                   "GObject-2.0")
     1209        if gtk3_ENABLED:
     1210            add_gi("Atk-1.0",
     1211                   "Notify-0.7",
     1212                   "fontconfig-2.0", "freetype2-2.0",
     1213                   "GDesktopEnums-3.0", "Soup-2.4",
     1214                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0",
     1215                   "HarfBuzz-0.0",
     1216                   "Libproxy-1.0", "libxml2-2.0",
     1217                   "cairo-1.0", "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1218                   "Rsvg-2.0",
     1219                   "win32-1.0")
     1220            if opengl_ENABLED:
     1221                add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1222            add_DLLs('visual', 'curl', 'soup', 'openjpeg')
     1223        if server_ENABLED and not PYTHON3:
     1224            add_DLLs('sqlite3')
     1225
     1226        if gtk2_ENABLED:
     1227            add_dir('lib',      {
     1228                "gdk-pixbuf-2.0":    {
     1229                    "2.10.0"    :   {
     1230                        "loaders"   :
     1231                            ["libpixbufloader-%s.dll" % x for x in ("ico", "jpeg", "svg", "bmp", "png",)]
     1232                        },
     1233                    },
     1234                })
     1235            if opengl_ENABLED:
     1236                add_DLLs("gtkglext-win32", "gdkglext-win32")
     1237            add_DLLs("gtk-win32", "gdk-win32",
     1238                     "gdk_pixbuf", "pyglib-2.0-python2")
     1239
     1240        if sound_ENABLED:
     1241            add_dir("share", ["gst-plugins-bad", "gst-plugins-base", "gstreamer-1.0"])
     1242            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1243                   "GstTag-1.0")
     1244            add_DLLs('gstreamer', 'orc-test')
     1245            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1246                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1247                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1248                      "basecamerabinsrc", "mpegts", "photography",
     1249                      ):
     1250                add_DLLs('gst%s' % p)
     1251            #DLLs needed by the plugins:
     1252            add_DLLs("faac", "faad", "flac", "mad", "mpg123")
     1253            #add the gstreamer plugins we need:
     1254            GST_PLUGINS = ("app",
     1255                           "cutter",
     1256                           #muxers:
     1257                           "gdp", "matroska", "ogg", "isomp4",
     1258                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1259                           "coreelements", "directsound", "directsoundsink", "directsoundsrc", "wasapi",
     1260                           #codecs:
     1261                           "opus", "opusparse", "flac", "lame", "mad", "mpg123", "speex", "faac", "faad",
     1262                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1263                           "autodetect",
     1264                           #untested: a52dec, voaacenc
     1265                           )
     1266            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1267            #END OF SOUND
     1268
     1269        if server_ENABLED:
     1270            #used by proxy server:
     1271            external_includes += ["multiprocessing", "setproctitle"]
     1272
     1273        external_includes += ["encodings"]
     1274        if client_ENABLED:
     1275            #for parsing "open-command":
     1276            external_includes += ["shlex"]
     1277            #for version check:
     1278            external_includes += [
     1279                                  "ftplib", "fileinput",
     1280                                  ]
     1281            if PYTHON3:
     1282                external_includes += ["urllib", "http.cookiejar", "http.client"]
     1283            else:
     1284                external_includes += ["urllib2", "cookielib", "httplib"]
     1285
     1286        if PYTHON3:
     1287            #hopefully, cx_Freeze will fix this horror:
     1288            #(we shouldn't have to deal with DLL dependencies)
     1289            import site
     1290            lib_python = os.path.dirname(site.getsitepackages()[0])
     1291            lib_dynload_dir = os.path.join(lib_python, "lib-dynload")
     1292            add_data_files('', glob.glob("%s/zlib*dll" % lib_dynload_dir))
     1293            for x in ("io", "codecs", "abc", "_weakrefset", "encodings"):
     1294                add_data_files("lib/", glob.glob("%s/%s*" % (lib_python, x)))
     1295        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1296        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1297        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL"]
     1298        cx_freeze_options = {
     1299                            "includes"          : external_includes,
     1300                            "packages"          : packages,
     1301                            "include_files"     : data_files,
     1302                            "excludes"          : excludes,
     1303                            "include_msvcr"     : True,
     1304                            "bin_excludes"      : bin_excludes,
     1305                            }
     1306        #cx_Freeze v5 workarounds:
     1307        if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
     1308            add_packages("numpy.core._methods", "numpy.lib.format")
     1309
     1310        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1311        executables = []
     1312        setup_options["executables"] = executables
     1313
     1314        def add_exe(script, icon, base_name, base="Console"):
     1315            executables.append(Executable(
     1316                        script                  = script,
     1317                        initScript              = None,
     1318                        #targetDir               = "dist",
     1319                        icon                    = "icons/%s" % icon,
     1320                        targetName              = "%s.exe" % base_name,
     1321                        base                    = base,
     1322                        ))
     1323
     1324        def add_console_exe(script, icon, base_name):
     1325            add_exe(script, icon, base_name)
     1326        def add_gui_exe(script, icon, base_name):
     1327            add_exe(script, icon, base_name, base="Win32GUI")
     1328        def add_service_exe(script, icon, base_name):
     1329            add_exe(script, icon, base_name, base="Win32Service")
     1330
     1331        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1332        if (client_ENABLED or server_ENABLED) and (gtk2_ENABLED or gtk3_ENABLED):
     1333            add_gui_exe("scripts/xpra",                         "xpra_txt.ico",     "Xpra")
     1334            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1335            add_console_exe("scripts/xpra_launcher",            "xpra.ico",         "Xpra-Launcher-Debug")
     1336            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1337            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1338            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1339        if server_ENABLED:
     1340            add_gui_exe("scripts/auth_dialog",                  "authentication.ico", "Auth_Dialog")
     1341        if gtk2_ENABLED:
     1342            #these need porting..
     1343            add_gui_exe("xpra/gtk_common/gtk_view_clipboard.py","clipboard.ico",    "GTK_Clipboard_Test")
     1344        if mdns_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1345            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1346        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1347        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1348        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1349        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1350        if gtk2_ENABLED or gtk3_ENABLED:
     1351            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1352            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1353            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1354            add_gui_exe("xpra/client/gtk_base/example/tray.py", "xpra.ico",         "SystemTray_Test")
     1355            add_gui_exe("xpra/client/gtk_base/u2f_tool.py",     "authentication.ico", "U2F_Tool")
     1356        if client_ENABLED or server_ENABLED:
     1357            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile")
     1358            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1359        if server_ENABLED:
     1360            add_console_exe("xpra/server/auth/sqlite_auth.py",  "sqlite.ico",        "SQLite_auth_tool")
     1361            add_console_exe("xpra/server/auth/win32_auth.py",   "authentication.ico", "System-Auth-Test")
     1362            add_console_exe("xpra/server/auth/ldap_auth.py",    "authentication.ico", "LDAP-Auth-Test")
     1363            add_console_exe("xpra/server/auth/ldap3_auth.py",   "authentication.ico", "LDAP3-Auth-Test")
     1364            add_console_exe("win32/service/proxy.py",           "xpra_txt.ico",      "Xpra-Proxy")
     1365            add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico",     "System-Logon-Test")
     1366        if client_ENABLED:
     1367            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1368            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1369            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1370        if client_ENABLED:
     1371            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1372            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1373        if sound_ENABLED:
     1374            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1375            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1376            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1377            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1378            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1379        if opengl_ENABLED:
     1380            if PYTHON3:
     1381                add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1382            else:
     1383                add_console_exe("xpra/client/gl/gtk_base/gtkgl_check.py", "opengl.ico", "OpenGL_check")
     1384        if webcam_ENABLED:
     1385            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1386            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1387        if printing_ENABLED:
     1388            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1389            add_console_exe("xpra/platform/win32/pdfium.py",    "printer.ico",     "PDFIUM_Print")
     1390            add_DLLs("pdfium")  #libpdfium.dll
     1391        if nvenc_ENABLED:
     1392            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1393        if nvfbc_ENABLED:
     1394            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1395        if nvfbc_ENABLED or nvenc_ENABLED:
     1396            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1397
     1398        if example_ENABLED:
     1399            add_gui_exe("xpra/client/gtk_base/example/colors.py",               "encoding.ico",     "Colors")
     1400            add_gui_exe("xpra/client/gtk_base/example/colors_gradient.py",      "encoding.ico",     "Colors-Gradient")
     1401            if not PYTHON3:
     1402                add_gui_exe("xpra/client/gtk_base/example/gl_colors_gradient.py",   "encoding.ico",     "OpenGL-Colors-Gradient")
     1403            add_gui_exe("xpra/client/gtk_base/example/colors_plain.py",         "encoding.ico",     "Colors-Plain")
     1404            add_gui_exe("xpra/client/gtk_base/example/bell.py",                 "bell.ico",         "Bell")
     1405            add_gui_exe("xpra/client/gtk_base/example/transparent_colors.py",   "transparent.ico",  "Transparent-Colors")
     1406            add_gui_exe("xpra/client/gtk_base/example/transparent_window.py",   "transparent.ico",  "Transparent-Window")
     1407            add_gui_exe("xpra/client/gtk_base/example/fontrendering.py",        "font.ico",         "Font-Rendering")
     1408
     1409        #FIXME: how do we figure out what target directory to use?
     1410        print("calling build_xpra_conf in-place")
     1411        #building etc files in-place:
     1412        build_xpra_conf(".")
     1413        add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1414        add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1415        add_data_files('etc/xpra', glob.glob("etc/xpra/nvfbc*.keys"))
     1416        add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1417        #build minified html5 client in temporary build dir:
     1418        if "clean" not in sys.argv and html5_ENABLED:
     1419            install_html5(os.path.join(install, "www"), )
     1420            for k,v in glob_recurse("build/www").items():
     1421                if (k!=""):
     1422                    k = os.sep+k
     1423                add_data_files('www'+k, v)
     1424
     1425    if client_ENABLED or server_ENABLED:
     1426        add_data_files(share_xpra,              ["win32/website.url"])
     1427        add_data_files('%sicons' % share_xpra,  glob.glob('icons\\*.ico'))
     1428
     1429    if webcam_ENABLED:
     1430        add_data_files(share_xpra,              ["win32\\DirectShow.tlb"])
     1431
    9071432    remove_packages(*external_excludes)
     1433    external_includes.append("pyu2f")
     1434    external_includes.append("mmap")
     1435    external_includes.append("comtypes")    #used by webcam and netdev_query
     1436    remove_packages("comtypes.gen")         #this is generated at runtime
     1437                                            #but we still have to remove the empty directory by hand
     1438                                            #afterwards because cx_freeze does weird things (..)
    9081439    remove_packages(#not used on win32:
    909                     "mmap",
    9101440                    #we handle GL separately below:
    9111441                    "OpenGL", "OpenGL_accelerate",
     
    9131443                    "ctypes.macholib")
    9141444
    915     if not cyxor_ENABLED or opengl_ENABLED:
     1445    if webcam_ENABLED and False:
     1446        external_includes.append("cv2")
     1447    else:
     1448        remove_packages("cv2")
     1449
     1450    if opengl_ENABLED or nvenc_ENABLED or nvfbc_ENABLED:
    9161451        #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1452        external_includes.append("numpy")
    9181453    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1454        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211455                        "pydoc")
    9221456
    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:
     1457    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1458    remove_packages("pygst", "gst", "gst.extend")
     1459
     1460    #add subset of PyOpenGL modules (only when installing):
     1461    if opengl_ENABLED and "install_exe" in sys.argv:
    9291462        #for this hack to work, you must add "." to the sys.path
    9301463        #so python can load OpenGL from the install directory
    9311464        #(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():
     1465        #but we re-add those two directories to the library.zip as part of the build script
     1466        import OpenGL
     1467        print("*** copying PyOpenGL modules to %s ***" % install)
     1468        glmodules = {
     1469            "OpenGL" : OpenGL,
     1470            }
     1471        try:
     1472            import OpenGL_accelerate        #@UnresolvedImport
     1473        except ImportError as e:
     1474            print("Warning: missing OpenGL_accelerate module")
     1475            print(" %s" % e)
     1476        else:
     1477            glmodules["OpenGL_accelerate"] = OpenGL_accelerate
     1478        for module_name, module in glmodules.items():
    9361479            module_dir = os.path.dirname(module.__file__ )
    9371480            try:
    9381481                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1482                    module_dir, os.path.join(install, "lib", module_name),
     1483                    ignore = shutil.ignore_patterns("Tk", "AGL", "EGL", "GLX", "GLX.*", "_GLX.*", "GLE", "GLES1", "GLES2", "GLES3")
    9411484                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1485                print("copied %s to %s/%s" % (module_dir, install, module_name))
     1486            except Exception as e:
     1487                if not isinstance(e, WindowsError) or (not "already exists" in str(e)): #@UndefinedVariable
    9441488                    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 
     1489
     1490        add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1491
     1492    #END OF win32
    9831493#*******************************************************************************
    9841494else:
    9851495    #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"]))
     1496    if LINUX:
     1497        if scripts_ENABLED:
     1498            scripts += ["scripts/xpra_udev_product_version", "scripts/xpra_signal_listener"]
     1499        libexec_scripts = []
     1500        if is_Fedora() or is_CentOS():
     1501            libexec = "libexec"
     1502        else:
     1503            libexec = "lib"
     1504        if xdg_open_ENABLED:
     1505            libexec_scripts += ["scripts/xdg-open", "scripts/gnome-open", "scripts/gvfs-open"]
     1506        if server_ENABLED:
     1507            libexec_scripts.append("scripts/auth_dialog")
     1508        if libexec_scripts:
     1509            add_data_files("%s/xpra/" % libexec, libexec_scripts)
     1510    man_path = "share/man"
     1511    if OPENBSD:
     1512        man_path = "man"
     1513    add_data_files("%s/man1" % man_path,  ["man/xpra.1", "man/xpra_launcher.1", "man/xpra_browser.1"])
     1514    add_data_files("share/applications",  ["xdg/xpra-shadow.desktop", "xdg/xpra-launcher.desktop", "xdg/xpra-browser.desktop", "xdg/xpra.desktop"])
     1515    add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1516    add_data_files("share/icons",         ["xdg/xpra.png", "xdg/xpra-mdns.png", "xdg/xpra-shadow.png"])
     1517    add_data_files("share/appdata",       ["xdg/xpra.appdata.xml"])
     1518
     1519    #here, we override build and install so we can
     1520    #generate our /etc/xpra/xpra.conf
     1521    class build_override(build):
     1522        def run(self):
     1523            build.run(self)
     1524            self.run_command("build_conf")
     1525
     1526    class build_conf(build):
     1527        def run(self):
     1528            try:
     1529                build_base = self.distribution.command_obj['build'].build_base
     1530            except:
     1531                build_base = self.build_base
     1532            build_xpra_conf(build_base)
     1533
     1534    class install_data_override(install_data):
     1535        def run(self):
     1536            print("install_data_override: install_dir=%s" % self.install_dir)
     1537            if html5_ENABLED:
     1538                install_html5(os.path.join(self.install_dir, "%swww" % share_xpra))
     1539            install_data.run(self)
     1540
     1541            root_prefix = self.install_dir.rstrip("/")
     1542            if root_prefix.endswith("/usr"):
     1543                root_prefix = root_prefix[:-4]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1544            build_xpra_conf(root_prefix)
     1545
     1546            def copytodir(src, dst_dir, dst_name=None, chmod=0o644):
     1547                #convert absolute paths:
     1548                if dst_dir.startswith("/"):
     1549                    dst_dir = root_prefix+dst_dir
     1550                else:
     1551                    dst_dir = self.install_dir.rstrip("/")+"/"+dst_dir
     1552                #make sure the target directory exists:
     1553                self.mkpath(dst_dir)
     1554                #generate the target filename:
     1555                filename = os.path.basename(src)
     1556                dst_file = os.path.join(dst_dir, dst_name or filename)
     1557                #copy it
     1558                print("copying %s -> %s (%s)" % (src, dst_dir, oct(chmod)))
     1559                shutil.copyfile(src, dst_file)
     1560                if chmod:
     1561                    os.chmod(dst_file, chmod)
     1562
     1563            if printing_ENABLED and POSIX:
     1564                #install "/usr/lib/cups/backend" with 0700 permissions:
     1565                copytodir("cups/xpraforwarder", "lib/cups/backend", chmod=0o700)
     1566
     1567            if x11_ENABLED:
     1568                #install xpra_Xdummy if we need it:
     1569                xvfb_command = detect_xorg_setup()
     1570                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])) or Xdummy_wrapper_ENABLED is True:
     1571                    copytodir("scripts/xpra_Xdummy", "bin", chmod=0o755)
     1572                #install xorg*.conf, cuda.conf and nvenc.keys:
     1573                etc_xpra_files = ["xorg.conf"]
     1574                if uinput_ENABLED:
     1575                    etc_xpra_files.append("xorg-uinput.conf")
     1576                if nvenc_ENABLED or nvfbc_ENABLED:
     1577                    etc_xpra_files.append("cuda.conf")
     1578                if nvenc_ENABLED:
     1579                    etc_xpra_files.append("nvenc.keys")
     1580                if nvfbc_ENABLED:
     1581                    etc_xpra_files.append("nvfbc.keys")
     1582                for x in etc_xpra_files:
     1583                    copytodir("etc/xpra/%s" % x, "/etc/xpra")
     1584                copytodir("etc/X11/xorg.conf.d/90-xpra-virtual.conf", "/etc/X11/xorg.conf.d/")
     1585
     1586            if pam_ENABLED:
     1587                copytodir("etc/pam.d/xpra", "/etc/pam.d")
     1588
     1589            systemd_dir = "/lib/systemd/system"
     1590            if service_ENABLED:
     1591                #Linux init service:
     1592                if os.path.exists("/bin/systemctl"):
     1593                    if sd_listen_ENABLED:
     1594                        copytodir("service/xpra.service", systemd_dir)
     1595                    else:
     1596                        copytodir("service/xpra-nosocketactivation.service", systemd_dir, dst_name="xpra.service")
     1597                else:
     1598                    copytodir("service/xpra", "/etc/init.d")
     1599                if os.path.exists("/etc/sysconfig"):
     1600                    copytodir("etc/sysconfig/xpra", "/etc/sysconfig")
     1601                elif os.path.exists("/etc/default"):
     1602                    copytodir("etc/sysconfig/xpra", "/etc/default")
     1603            if sd_listen_ENABLED:
     1604                copytodir("service/xpra.socket", systemd_dir)
     1605            if dbus_ENABLED and proxy_ENABLED:
     1606                copytodir("dbus/xpra.conf", "/etc/dbus-1/system.d")
     1607
     1608
     1609    # add build_conf to build step
     1610    cmdclass.update({
     1611             'build'        : build_override,
     1612             'build_conf'   : build_conf,
     1613             'install_data' : install_data_override,
     1614             })
    9971615
    9981616    if OSX:
     1617        #pyobjc needs email.parser
     1618        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
     1619        external_includes += ["kerberos", "future", "pyu2f", "paramiko", "nacl"]
     1620        if not PYTHON3:
     1621            external_includes += ["urllib2"]
    9991622        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001623        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011624        add_packages("xpra.platform.darwin")
     1625        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1626        #for u2f on python2:
     1627        if not PYTHON3:
     1628            modules.append("UserList")
     1629            modules.append("UserString")
     1630        #to support GStreamer 1.x we need this:
     1631        modules.append("importlib")
    10021632    else:
    10031633        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041634        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")
     1635        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1636        #not supported by all distros, but doesn't hurt to install them anyway:
     1637        for x in ("tmpfiles.d", "sysusers.d"):
     1638            add_data_files("lib/%s" % x, ["%s/xpra.conf" % x])
     1639        if uinput_ENABLED:
     1640            add_data_files("lib/udev/rules.d/", ["udev/rules.d/71-xpra-virtual-pointer.rules"])
    10081641
    10091642    #gentoo does weird things, calls --no-compile with build *and* install
     
    10121645    #otherwise we use the flags to skip pkgconfig
    10131646    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))
     1647        pkgconfig = no_pkgconfig
    10331648
    10341649    if OSX and "py2app" in sys.argv:
     
    10451660        remove_packages(*external_excludes)
    10461661
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1662        try:
     1663            from xpra.src_info import REVISION
     1664        except:
     1665            REVISION = "unknown"
     1666        Plist = {
     1667            "CFBundleDocumentTypes" : {
     1668                "CFBundleTypeExtensions"    : ["Xpra"],
     1669                "CFBundleTypeName"          : "Xpra Session Config File",
     1670                "CFBundleName"              : "Xpra",
     1671                "CFBundleTypeRole"          : "Viewer",
     1672                },
     1673            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2018 http://xpra.org/" % (XPRA_VERSION, REVISION),
     1674            "CFBundleIdentifier"            : "org.xpra.xpra",
     1675            }
    10531676        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541677        #so the make-app.sh script still has to hack around this problem.
    10551678        add_modules(*external_includes)
     1679        #needed by python-lz4:
     1680        add_modules("distutils")
    10561681        py2app_options = {
    10571682            'iconfile'          : '../osx/xpra.icns',
     
    10651690            }
    10661691        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1692        setup_options["app"]     = ["xpra/client/gtk_base/client_launcher.py"]
     1693
     1694    if OSX:
     1695        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1696        #so instead we have to query each package seperately and merge the results:
     1697        def osx_pkgconfig(*pkgs_options, **ekw):
     1698            kw = dict(ekw)
     1699            for pkg in pkgs_options:
     1700                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1701                if pkg.lower().startswith("x"):
     1702                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1703                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1704                kw = exec_pkgconfig(pkg, **kw)
     1705                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1706            return kw
     1707
     1708        pkgconfig = osx_pkgconfig
     1709
     1710
     1711if scripts_ENABLED:
     1712    scripts += ["scripts/xpra", "scripts/xpra_launcher", "scripts/xpra_browser"]
     1713
     1714add_data_files(share_xpra,                      ["README", "COPYING"])
     1715add_data_files(share_xpra,                      ["bell.wav"])
     1716add_data_files("%shttp-headers" % share_xpra,   glob.glob("http-headers/*"))
     1717add_data_files("%sicons" % share_xpra,          glob.glob("icons/*png"))
     1718add_data_files("%scontent-type" % share_xpra,   glob.glob("content-type/*"))
    10681719
    10691720
    10701721if 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 
     1722    if WIN32 or OSX:
     1723        external_includes.append("websockify")
     1724        external_includes.append("numpy")
     1725        external_includes.append("ssl")
     1726        external_includes.append("_ssl")
     1727        if not PYTHON3:
     1728            external_includes.append("mimetypes")
     1729            external_includes.append("mimetools")
     1730            external_includes.append("BaseHTTPServer")
     1731
     1732
     1733if annotate_ENABLED:
     1734    from Cython.Compiler import Options
     1735    Options.annotate = True
    10761736
    10771737
    10781738#*******************************************************************************
    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")
     1739buffers_c = "xpra/buffers/buffers.c"
     1740memalign_c = "xpra/buffers/memalign.c"
     1741xxhash_c = "xpra/buffers/xxhash.c"
     1742membuffers_c = [memalign_c, buffers_c, xxhash_c]
     1743
     1744add_packages("xpra.buffers")
     1745buffers_pkgconfig = pkgconfig(optimize=3)
     1746cython_add(Extension("xpra.buffers.membuf",
     1747            ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1748
     1749
     1750toggle_packages(dbus_ENABLED, "xpra.dbus")
     1751toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1752toggle_packages(server_ENABLED or proxy_ENABLED, "xpra.server", "xpra.server.auth")
     1753toggle_packages(rfb_ENABLED, "xpra.server.rfb")
     1754toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1755toggle_packages(server_ENABLED, "xpra.server.window")
     1756toggle_packages(server_ENABLED or shadow_ENABLED, "xpra.server.mixins", "xpra.server.source")
     1757toggle_packages(shadow_ENABLED, "xpra.server.shadow")
     1758toggle_packages(server_ENABLED or client_ENABLED, "xpra.clipboard")
     1759toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1760toggle_packages(notifications_ENABLED, "xpra.notifications")
     1761
     1762#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1763if dbus_ENABLED and server_ENABLED:
     1764    add_packages("xpra.server.dbus")
     1765
     1766if OSX:
     1767    if PYTHON3:
     1768        quartz_pkgconfig = pkgconfig("gtk+-3.0", "pygobject-3.0")
     1769        add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1770                    "-ObjC",
     1771                    "-framework", "AppKit",
     1772                    "-I/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/")
     1773        cython_add(Extension("xpra.platform.darwin.gdk3_bindings",
     1774                ["xpra/platform/darwin/gdk3_bindings.pyx"],
     1775                language="objc",
     1776                **quartz_pkgconfig
     1777                ))
     1778    else:
     1779        quartz_pkgconfig = pkgconfig(*PYGTK_PACKAGES)
     1780        add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1781                    '-mmacosx-version-min=10.10',
     1782                    '-framework', 'Foundation',
     1783                    '-framework', 'AppKit',
     1784                    '-ObjC',
     1785                    "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h")
     1786        cython_add(Extension("xpra.platform.darwin.gdk_bindings",
     1787                ["xpra/platform/darwin/gdk_bindings.pyx", "xpra/platform/darwin/nsevent_glue.m"],
     1788                language="objc",
     1789                **quartz_pkgconfig
     1790                ))
     1791
     1792monotonic_time_pkgconfig = pkgconfig()
     1793if not OSX and not WIN32 and not OPENBSD:
     1794    add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1795cython_add(Extension("xpra.monotonic_time",
     1796            ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1797            **monotonic_time_pkgconfig
     1798            ))
     1799
     1800
     1801toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841802if x11_ENABLED:
    10851803    make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
     1804    if gtk2_ENABLED:
     1805        make_constants("xpra", "x11", "constants", pxi_file="xpra/x11/gtk2/constants.pxi")
     1806    if gtk3_ENABLED:
     1807        make_constants("xpra", "x11", "constants", pxi_file="xpra/x11/gtk3/constants.pxi")
    10871808
    10881809    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
     
    10981819                **pkgconfig("x11")
    10991820                ))
     1821    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1822                ["xpra/x11/bindings/posix_display_source.pyx"],
     1823                **pkgconfig("x11")
     1824                ))
     1825
    11001826    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011827                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041830    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051831                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1832                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071833                ))
    11081834
    11091835    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101836                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1837                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121838                ))
    11131839    cython_add(Extension("xpra.x11.bindings.ximage",
    11141840                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1841                **pkgconfig("x11", "xext", "xcomposite")
    11161842                ))
    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)
     1843if xinput_ENABLED:
     1844    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1845                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1846                **pkgconfig("x11", "xi")
    11221847                ))
    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)
     1848
     1849toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1850if gtk_x11_ENABLED:
     1851    toggle_packages(PYTHON3, "xpra.x11.gtk3")
     1852    toggle_packages(not PYTHON3, "xpra.x11.gtk2", "xpra.x11.gtk2.models")
     1853    if PYTHON3:
     1854        #GTK3 display source:
     1855        cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1856                    ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1857                    **pkgconfig("gdk-3.0")
     1858                    ))
     1859        cython_add(Extension("xpra.x11.gtk3.gdk_bindings",
     1860                    ["xpra/x11/gtk3/gdk_bindings.pyx", "xpra/x11/gtk3/gdk_x11_macros.c"],
     1861                    **pkgconfig("gdk-3.0")
     1862                    ))
     1863
     1864    else:
     1865        #GTK2:
     1866        cython_add(Extension("xpra.x11.gtk2.gdk_display_source",
     1867                    ["xpra/x11/gtk2/gdk_display_source.pyx"],
     1868                    **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1869                    ))
     1870        GDK_BINDINGS_PACKAGES = PYGTK_PACKAGES + ["x11", "xext", "xfixes", "xdamage"]
     1871        cython_add(Extension("xpra.x11.gtk2.gdk_bindings",
     1872                    ["xpra/x11/gtk2/gdk_bindings.pyx"],
     1873                    **pkgconfig(*GDK_BINDINGS_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1874                    ))
     1875
     1876toggle_packages(not PYTHON3 and (gtk2_ENABLED or gtk_x11_ENABLED), "xpra.gtk_common.gtk2")
     1877if gtk2_ENABLED or (gtk_x11_ENABLED and not PYTHON3):
     1878    cython_add(Extension("xpra.gtk_common.gtk2.gdk_bindings",
     1879                ["xpra/gtk_common/gtk2/gdk_bindings.pyx"],
     1880                **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
    11271881                ))
    1128 
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1882elif gtk3_ENABLED or (gtk_x11_ENABLED and PYTHON3):
     1883    cython_add(Extension("xpra.gtk_common.gtk3.gdk_bindings",
     1884                ["xpra/gtk_common/gtk3/gdk_bindings.pyx"],
     1885                **pkgconfig("gtk+-3.0", "pygobject-3.0")
     1886                ))
     1887
     1888if client_ENABLED and gtk3_ENABLED:
     1889    #cairo workaround:
     1890    if OSX:
     1891        pycairo = "py3cairo"
     1892    else:
     1893        pycairo = "pycairo"
     1894    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1895                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1896                **pkgconfig(pycairo)
     1897                ))
     1898
     1899if client_ENABLED or server_ENABLED:
     1900    add_packages("xpra.codecs.argb")
     1901    argb_pkgconfig = pkgconfig(optimize=3)
    11321902    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1903                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1904
     1905
     1906#build tests, but don't install them:
     1907toggle_packages(tests_ENABLED, "unit")
    11341908
    11351909
    11361910if bundle_tests_ENABLED:
    11371911    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
     1912    for k,v in glob_recurse("unit").items():
    11391913        if (k!=""):
    11401914            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:
     1915        add_data_files("unit"+k, v)
     1916
     1917#python-cryptography needs workarounds for bundling:
     1918if crypto_ENABLED and (OSX or WIN32):
     1919    external_includes.append("_ssl")
     1920    external_includes.append("cffi")
     1921    external_includes.append("_cffi_backend")
     1922    external_includes.append("cryptography")
     1923    external_includes.append("idna")
     1924    external_includes.append("idna.idnadata")
     1925    external_includes.append("pkg_resources._vendor.packaging")
     1926    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1927    external_includes.append("pkg_resources._vendor.pyparsing")
     1928    add_modules("cryptography.hazmat.bindings._openssl")
     1929    add_modules("cryptography.hazmat.bindings._constant_time")
     1930    add_modules("cryptography.hazmat.bindings._padding")
     1931    add_modules("cryptography.hazmat.backends.openssl")
     1932    add_modules("cryptography.fernet")
     1933    if WIN32:
     1934        external_includes.append("appdirs")
     1935
     1936#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441937if 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")
     1938    add_modules("xpra.client", "xpra.client.mixins")
     1939    add_modules("xpra.scripts.gtk_info")
     1940    add_modules("xpra.scripts.show_webcam")
     1941toggle_packages((client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED)) or (PYTHON3 and sound_ENABLED) or server_ENABLED, "xpra.gtk_common")
    11471942toggle_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")
     1943toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1944toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and (MINGW_PREFIX or PYTHON3)), "gi")
    11501945toggle_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")
     1946toggle_packages(client_ENABLED and opengl_ENABLED and gtk2_ENABLED, "xpra.client.gl.gtk2")
     1947toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1948toggle_packages(client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED) and example_ENABLED, "xpra.client.gtk_base.example")
     1949if client_ENABLED and WIN32 and MINGW_PREFIX:
     1950    propsys_pkgconfig = pkgconfig()
     1951    if debug_ENABLED:
     1952        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1953    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1954    cython_add(Extension("xpra.platform.win32.propsys",
     1955                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1956                language="c++",
     1957                **propsys_pkgconfig))
     1958
     1959if client_ENABLED or server_ENABLED:
     1960    add_modules("xpra.codecs")
     1961toggle_packages(keyboard_ENABLED, "xpra.keyboard")
     1962if client_ENABLED or server_ENABLED:
     1963    add_modules("xpra.scripts.config", "xpra.scripts.parsing", "xpra.scripts.exec_util", "xpra.scripts.fdproxy", "xpra.scripts.version")
     1964if server_ENABLED or proxy_ENABLED:
     1965    add_modules("xpra.scripts.server")
     1966if WIN32 and client_ENABLED and (gtk2_ENABLED or gtk3_ENABLED):
     1967    add_modules("xpra.scripts.gtk_info")
     1968
     1969toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1970#we can't just include "xpra.client.gl" because cx_freeze then do the wrong thing
     1971#and tries to include both gtk3 and gtk2, and fails hard..
     1972for x in ("gl_check", "gl_colorspace_conversions", "gl_window_backing_base", "gl_drivers"):
     1973    toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl.%s" % x)
     1974toggle_packages(client_ENABLED and opengl_ENABLED and (gtk2_ENABLED or gtk3_ENABLED), "xpra.client.gl.gtk_base")
     1975
     1976
     1977toggle_modules(sound_ENABLED, "xpra.sound")
     1978toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541979
    11551980toggle_packages(clipboard_ENABLED, "xpra.clipboard")
    11561981if 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:
     1982    if PYTHON3:
     1983        cython_add(Extension("xpra.gtk_common.gtk3.gdk_atoms",
     1984                             ["xpra/gtk_common/gtk3/gdk_atoms.pyx"],
     1985                             **pkgconfig("gtk+-3.0")
     1986                             ))
     1987    else:
     1988        cython_add(Extension("xpra.gtk_common.gtk2.gdk_atoms",
     1989                             ["xpra/gtk_common/gtk2/gdk_atoms.pyx"],
     1990                             **pkgconfig(*PYGTK_PACKAGES, ignored_tokens=gtk2_ignored_tokens)
     1991                             ))
     1992
     1993toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     1994if client_ENABLED or server_ENABLED:
    11631995    cython_add(Extension("xpra.codecs.xor.cyxor",
    11641996                ["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")
     1997                **pkgconfig(optimize=3)))
     1998
     1999if server_ENABLED or shadow_ENABLED:
     2000    O3_pkgconfig = pkgconfig(optimize=3)
     2001    cython_add(Extension("xpra.server.cystats",
     2002                ["xpra/server/cystats.pyx"],
     2003                **O3_pkgconfig))
     2004    cython_add(Extension("xpra.server.window.region",
     2005                ["xpra/server/window/region.pyx"],
     2006                **O3_pkgconfig))
     2007    cython_add(Extension("xpra.server.window.motion",
     2008                ["xpra/server/window/motion.pyx"],
     2009                **O3_pkgconfig))
     2010
     2011if sd_listen_ENABLED:
     2012    sdp = pkgconfig("libsystemd")
     2013    cython_add(Extension("xpra.platform.xposix.sd_listen",
     2014                ["xpra/platform/xposix/sd_listen.pyx"],
     2015                **sdp))
     2016
     2017
    11782018toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11792019
     2020toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     2021if nvfbc_ENABLED:
     2022    nvfbc_pkgconfig = pkgconfig("nvfbc")
     2023    if WIN32:
     2024        add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     2025    platform = sys.platform.rstrip("0123456789")
     2026    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture_%s" % platform,
     2027                         ["xpra/codecs/nvfbc/fbc_capture_%s.pyx" % platform],
     2028                         language="c++",
     2029                         **nvfbc_pkgconfig))
     2030
    11802031toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
     2032toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     2033toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     2034
     2035if nvenc_ENABLED and cuda_kernels_ENABLED:
     2036    #find nvcc:
     2037    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     2038    if WIN32:
     2039        nvcc_exe = "nvcc.exe"
     2040        CUDA_DIR = os.environ.get("CUDA_DIR", "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA")
     2041        path_options = [os.path.join(CUDA_DIR, x, "bin") for x in ("v9.2", "v9.1", "v9.0", "v8.0", "v7.5")] + path_options
     2042        #pycuda may link against curand, find it and ship it:
     2043        for p in path_options:
     2044            if os.path.exists(p):
     2045                add_data_files("", glob.glob("%s\\curand64*.dll" % p))
     2046                add_data_files("", glob.glob("%s\\cudart64*.dll" % p))
     2047                break
     2048    else:
     2049        nvcc_exe = "nvcc"
     2050        for v in ("", "10.0", "9.2", "9.1", "-9.0", "-8.0", "-7.5"):
     2051            path_options += ["/usr/local/cuda%s/bin" % v, "/opt/cuda%s/bin" % v]
     2052    options = [os.path.join(x, nvcc_exe) for x in path_options]
     2053    def which(cmd):
     2054        try:
     2055            code, out, _ = get_status_output(["which", cmd])
     2056            if code==0:
     2057                return out
     2058        except:
     2059            pass
     2060    #prefer the one we find on the $PATH, if any:
     2061    try:
     2062        v = which(nvcc_exe)
     2063        if v and (v not in options):
     2064            options.insert(0, v)
     2065    except:
     2066        pass
     2067    nvcc_versions = {}
     2068    for filename in options:
     2069        if not os.path.exists(filename):
     2070            continue
     2071        code, out, err = get_status_output([filename, "--version"])
     2072        if code==0:
     2073            vpos = out.rfind(", V")
     2074            if vpos>0:
     2075                version = out[vpos+3:].strip("\n")
     2076                version_str = " version %s" % version
     2077            else:
     2078                version = "0"
     2079                version_str = " unknown version!"
     2080            print("found CUDA compiler: %s%s" % (filename, version_str))
     2081            vnum = tuple(int(x) for x in version.split("."))
     2082            nvcc_versions[vnum] = filename
     2083    assert nvcc_versions, "cannot find nvcc compiler!"
     2084    #choose the most recent one:
     2085    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     2086    if len(nvcc_versions)>1:
     2087        print(" using version %s from %s" % (version, nvcc))
     2088    if WIN32:
     2089        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     2090        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     2091    #first compile the cuda kernels
     2092    #(using the same cuda SDK for both nvenc modules for now..)
     2093    #TODO:
     2094    # * compile directly to output directory instead of using data files?
     2095    # * detect which arches we want to build for? (does it really matter much?)
     2096    kernels = ("ARGB_to_NV12", "ARGB_to_YUV444", "BGRA_to_NV12", "BGRA_to_YUV444")
     2097    for kernel in kernels:
     2098        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     2099        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     2100        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     2101            continue
     2102        reason = should_rebuild(cuda_src, cuda_bin)
     2103        if not reason:
     2104            continue
     2105        print("rebuilding %s: %s" % (kernel, reason))
     2106        cmd = [nvcc,
     2107               '-fatbin',
     2108               #"-cubin",
     2109               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     2110               #"-gencode=arch=compute_50,code=sm_50",
     2111               #"-gencode=arch=compute_52,code=sm_52",
     2112               #"-gencode=arch=compute_52,code=compute_52",
     2113               "-c", cuda_src,
     2114               "-o", cuda_bin]
     2115        #GCC 8.1 has compatibility issues with CUDA 9.2,
     2116        #so revert to C++03:
     2117        if get_gcc_version()>=[8, 1]:
     2118            cmd.append("-std=c++03")
     2119        #GCC 6 uses C++11 by default:
     2120        elif get_gcc_version()>=[6, 0]:
     2121            cmd.append("-std=c++11")
     2122        CL_VERSION = os.environ.get("CL_VERSION")
     2123        if CL_VERSION:
     2124            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     2125            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     2126            cmd += ["--machine", "32"]
     2127        if WIN32:
     2128            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     2129            #cmd += ["--input-drive-prefix", "/"]
     2130            #cmd += ["--dependency-drive-prefix", "/"]
     2131            cmd += ["-I%s" % os.path.abspath("win32")]
     2132        comp_code_options = [(30, 30), (35, 35)]
     2133        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     2134        if version!=(0,) and version<(7, 5):
     2135            print("CUDA version %s is very unlikely to work")
     2136            print("try upgrading to version 7.5 or later")
     2137        if version>=(7, 5):
     2138            comp_code_options.append((50, 50))
     2139            comp_code_options.append((52, 52))
     2140            comp_code_options.append((53, 53))
     2141        if version>=(8, 0):
     2142            comp_code_options.append((60, 60))
     2143            comp_code_options.append((61, 61))
     2144            comp_code_options.append((62, 62))
     2145        if version>=(9, 0):
     2146            comp_code_options.append((70, 70))
     2147        if version>=(10, 0):
     2148            comp_code_options.append((75, 75))
     2149        for arch, code in comp_code_options:
     2150            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     2151        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     2152        print(" %s" % " ".join("'%s'" % x for x in cmd))
     2153        c, stdout, stderr = get_status_output(cmd)
     2154        if c!=0:
     2155            print("Error: failed to compile CUDA kernel %s" % kernel)
     2156            print(stdout or "")
     2157            print(stderr or "")
     2158            sys.exit(1)
     2159    CUDA_BIN = "%scuda" % share_xpra
     2160    if WIN32:
     2161        CUDA_BIN = "CUDA"
     2162    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2163
    11812164if 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))
     2165    nvencmodule = "nvenc"
     2166    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2167    #don't link against libnvidia-encode, we load it dynamically:
     2168    libraries = nvenc_pkgconfig.get("libraries", [])
     2169    if "nvidia-encode" in libraries:
     2170        libraries.remove("nvidia-encode")
     2171    if PYTHON3 and get_gcc_version()>=[6, 2]:
     2172        #with gcc 6.2 on Fedora:
     2173        #xpra/codecs/nvenc/encoder.c: In function '__Pyx_PyInt_LshiftObjC':
     2174        #xpra/codecs/nvenc/encoder.c:45878:34: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
     2175        #    if (unlikely(!(b < sizeof(long)*8 && a == x >> b)) && a) {
     2176        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-sign-compare")
     2177    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2178                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2179                         **nvenc_pkgconfig))
    11872180
    11882181toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892182if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2183    x264_pkgconfig = pkgconfig("x264")
     2184    if get_gcc_version()>=[6, 0]:
     2185        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912186    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922187                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2188                **x264_pkgconfig))
    11942189
    11952190toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962191if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2192    x265_pkgconfig = pkgconfig("x265")
    11982193    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992194                ["xpra/codecs/enc_x265/encoder.pyx"],
    1200                 **x265_pkgconfig), min_version=(0, 16))
     2195                **x265_pkgconfig))
     2196
     2197toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2198if pillow_ENABLED:
     2199    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
    12012200
    12022201toggle_packages(webp_ENABLED, "xpra.codecs.webp")
     
    12042203    webp_pkgconfig = pkgconfig("webp")
    12052204    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))
     2205                    ["xpra/codecs/webp/encode.pyx"],
     2206                    **webp_pkgconfig))
     2207    cython_add(Extension("xpra.codecs.webp.decode",
     2208                ["xpra/codecs/webp/decode.pyx"],
     2209                **webp_pkgconfig))
     2210
     2211toggle_packages(jpeg_ENABLED, "xpra.codecs.jpeg")
     2212if jpeg_ENABLED:
     2213    jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2214    cython_add(Extension("xpra.codecs.jpeg.encoder",
     2215                ["xpra/codecs/jpeg/encoder.pyx"],
     2216                **jpeg_pkgconfig))
     2217    cython_add(Extension("xpra.codecs.jpeg.decoder",
     2218                ["xpra/codecs/jpeg/decoder.pyx"],
     2219                **jpeg_pkgconfig))
     2220
     2221#swscale and avcodec2 use libav_common/av_log:
     2222libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2223toggle_packages(libav_common, "xpra.codecs.libav_common")
     2224if libav_common:
     2225    avutil_pkgconfig = pkgconfig("avutil")
     2226    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2227                ["xpra/codecs/libav_common/av_log.pyx"],
     2228                **avutil_pkgconfig))
     2229
    12162230
    12172231toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182232if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2233    avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", "avformat")
    12202234    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 
     2235                ["xpra/codecs/dec_avcodec2/decoder.pyx", "xpra/codecs/dec_avcodec2/register_compat.c"],
     2236                **avcodec2_pkgconfig))
     2237
     2238
     2239toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2240if csc_libyuv_ENABLED:
     2241    libyuv_pkgconfig = pkgconfig("libyuv")
     2242    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2243                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2244                language="c++",
     2245                **libyuv_pkgconfig))
    12242246
    12252247toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262248if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2249    swscale_pkgconfig = pkgconfig("swscale", "avutil")
    12292250    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))
     2251                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2252                **swscale_pkgconfig))
     2253
    12392254
    12402255toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412256if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2257    vpx_pkgconfig = pkgconfig("vpx")
    12432258    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))
     2259                ["xpra/codecs/vpx/encoder.pyx"],
     2260                **vpx_pkgconfig))
    12462261    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))
     2262                ["xpra/codecs/vpx/decoder.pyx"],
     2263                **vpx_pkgconfig))
     2264
     2265toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2266if enc_ffmpeg_ENABLED:
     2267    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2268    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2269                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2270                **ffmpeg_pkgconfig))
     2271
     2272toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2273if v4l2_ENABLED:
     2274    v4l2_pkgconfig = pkgconfig()
     2275    #fuly warning: cython makes this difficult,
     2276    #we have to figure out if "device_caps" exists in the headers:
     2277    ENABLE_DEVICE_CAPS = False
     2278    if os.path.exists("/usr/include/linux/videodev2.h"):
     2279        hdata = open("/usr/include/linux/videodev2.h").read()
     2280        ENABLE_DEVICE_CAPS = hdata.find("device_caps")>=0
     2281    kwargs = {"ENABLE_DEVICE_CAPS" : ENABLE_DEVICE_CAPS}
     2282    make_constants("xpra", "codecs", "v4l2", "constants", **kwargs)
     2283    cython_add(Extension("xpra.codecs.v4l2.pusher",
     2284                ["xpra/codecs/v4l2/pusher.pyx"],
     2285                **v4l2_pkgconfig))
    12622286
    12632287
    12642288toggle_packages(bencode_ENABLED, "xpra.net.bencode")
     2289toggle_packages(bencode_ENABLED and cython_bencode_ENABLED, "xpra.net.bencode.cython_bencode")
    12652290if 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")
     2291    bencode_pkgconfig = pkgconfig(optimize=3)
    12722292    cython_add(Extension("xpra.net.bencode.cython_bencode",
    12732293                ["xpra/net/bencode/cython_bencode.pyx"],
    12742294                **bencode_pkgconfig))
    12752295
     2296if netdev_ENABLED:
     2297    netdev_pkgconfig = pkgconfig()
     2298    cython_add(Extension("xpra.platform.xposix.netdev_query",
     2299                ["xpra/platform/xposix/netdev_query.pyx"],
     2300                **netdev_pkgconfig))
     2301
     2302if vsock_ENABLED:
     2303    vsock_pkgconfig = pkgconfig()
     2304    cython_add(Extension("xpra.net.vsock",
     2305                ["xpra/net/vsock.pyx"],
     2306                **vsock_pkgconfig))
     2307
     2308if pam_ENABLED:
     2309    pam_pkgconfig = pkgconfig()
     2310    add_to_keywords(pam_pkgconfig, 'extra_compile_args', "-I/usr/include/pam", "-I/usr/include/security")
     2311    add_to_keywords(pam_pkgconfig, 'extra_link_args', "-lpam", "-lpam_misc")
     2312    cython_add(Extension("xpra.server.pam",
     2313                ["xpra/server/pam.pyx"],
     2314                **pam_pkgconfig))
     2315
    12762316
    12772317if ext_modules:
    1278     setup_options["ext_modules"] = ext_modules
     2318    from Cython.Build import cythonize
     2319    #this causes Cython to fall over itself:
     2320    #gdb_debug=debug_ENABLED
     2321    setup_options["ext_modules"] = cythonize(ext_modules, gdb_debug=False)
    12792322if cmdclass:
    12802323    setup_options["cmdclass"] = cmdclass
     
    12832326
    12842327
    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 
    12932328def main():
    12942329    if OSX or WIN32 or debug_ENABLED:
     2330        print()
    12952331        print("setup options:")
     2332        if verbose_ENABLED:
     2333            print("setup_options=%s" % (setup_options,))
     2334        try:
     2335            from xpra.util import repr_ellipsized as pv
     2336        except:
     2337            def pv(v):
     2338                return str(v)
    12962339        for k,v in setup_options.items():
    1297             print_option("", k, v)
     2340            print_option("", k, pv(v))
    12982341        print("")
    12992342
Note: See TracChangeset for help on using the changeset viewer.