xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/setup.py

    r6000 r28374  
    22
    33# This file is part of Xpra.
    4 # Copyright (C) 2010-2014 Antoine Martin <antoine@devloop.org.uk>
     4# Copyright (C) 2010-2019 Antoine Martin <antoine@xpra.org>
    55# Copyright (C) 2008, 2009, 2010 Nathaniel Smith <njs@pobox.com>
    66# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     
    88
    99##############################################################################
    10 # FIXME: Cython.Distutils.build_ext leaves crud in the source directory.  (So
    11 # does the make_constants hack.)
    12 
    13 import commands
     10# FIXME: Cython.Distutils.build_ext leaves crud in the source directory.
     11
     12import ssl
     13import sys
     14if sys.version_info<(3, 6):
     15    raise Exception("xpra no longer supports Python versions older than 3.6")
     16
    1417import glob
     18import shutil
     19import os.path
     20
    1521from distutils.core import setup
    1622from distutils.extension import Extension
    17 import subprocess, sys, traceback
    18 import os.path
    19 import stat
     23from distutils.command.build import build
     24from distutils.command.install_data import install_data
     25
     26import xpra
     27from xpra.os_util import (
     28    get_status_output, load_binary_file,
     29    BITS, WIN32, OSX, LINUX, POSIX, NETBSD, FREEBSD, OPENBSD,
     30    is_Ubuntu, is_Debian, is_Fedora, is_CentOS, is_RedHat,
     31    )
     32
     33#we don't support versions of Python without the new ssl code:
     34if not hasattr(ssl, "SSLContext"):
     35    print("Warning: xpra requires a Python version with ssl.SSLContext support")
     36    print(" SSL support will not be available!")
    2037
    2138print(" ".join(sys.argv))
    2239
    23 import xpra
    24 from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
    25 
    26 WIN32 = sys.platform.startswith("win")
    27 OSX = sys.platform.startswith("darwin")
    28 
    29 
     40#*******************************************************************************
     41# build options, these may get modified further down..
     42#
     43data_files = []
     44modules = []
     45packages = []       #used by py2app
     46excludes = []       #only used by cx_freeze on win32
     47ext_modules = []
     48cmdclass = {}
     49scripts = []
     50description = "multi-platform screen and application forwarding system"
     51long_description = "Xpra is a multi platform persistent remote display server and client for " + \
     52            "forwarding applications and desktop screens. Also known as 'screen for X11'."
     53url = "https://xpra.org/"
     54
     55
     56XPRA_VERSION = xpra.__version__         #@UndefinedVariable
     57setup_options = {
     58                 "name"             : "xpra",
     59                 "version"          : XPRA_VERSION,
     60                 "license"          : "GPLv2+",
     61                 "author"           : "Antoine Martin",
     62                 "author_email"     : "antoine@xpra.org",
     63                 "url"              : url,
     64                 "download_url"     : "https://xpra.org/src/",
     65                 "description"      : description,
     66                 "long_description" : long_description,
     67                 "data_files"       : data_files,
     68                 "py_modules"       : modules,
     69                 }
     70
     71
     72if "pkg-info" in sys.argv:
     73    with open("PKG-INFO", "wb") as f:
     74        pkg_info_values = setup_options.copy()
     75        pkg_info_values.update({
     76                                "metadata_version"  : "1.1",
     77                                "summary"           :  description,
     78                                "home_page"         : url,
     79                                })
     80        for k in ("Metadata-Version", "Name", "Version", "Summary", "Home-page",
     81                  "Author", "Author-email", "License", "Download-URL", "Description"):
     82            v = pkg_info_values[k.lower().replace("-", "_")]
     83            f.write(b"%s: %s\n" % (k, v))
     84    sys.exit(0)
     85
     86
     87print("Xpra version %s" % XPRA_VERSION)
    3088#*******************************************************************************
    3189# Most of the options below can be modified on the command line
     
    3391# only the default values are specified here:
    3492#*******************************************************************************
     93
     94PKG_CONFIG = os.environ.get("PKG_CONFIG", "pkg-config")
     95has_pkg_config = False
     96if PKG_CONFIG:
     97    v = get_status_output([PKG_CONFIG, "--version"])
     98    has_pkg_config = v[0]==0 and v[1]
     99    if has_pkg_config:
     100        print("found pkg-config version: %s" % v[1].strip("\n\r"))
     101    else:
     102        print("WARNING: pkg-config not found!")
     103
     104for arg in list(sys.argv):
     105    if arg.startswith("--pkg-config-path="):
     106        pcp = arg[len("--pkg-config-path="):]
     107        pcps = [pcp] + os.environ.get("PKG_CONFIG_PATH", "").split(os.path.pathsep)
     108        os.environ["PKG_CONFIG_PATH"] = os.path.pathsep.join([x for x in pcps if x])
     109        print("using PKG_CONFIG_PATH=%s" % (os.environ["PKG_CONFIG_PATH"], ))
     110        sys.argv.remove(arg)
     111
     112def no_pkgconfig(*_pkgs_options, **_ekw):
     113    return {}
     114
    35115def 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 
     116    return get_status_output([PKG_CONFIG] + [str(x) for x in args])[0]==0
     117
     118def pkg_config_version(req_version, pkgname):
     119    cmd = [PKG_CONFIG, "--modversion", pkgname]
     120    r, out, _ = get_status_output(cmd)
     121    if r!=0 or not out:
     122        return False
     123    from distutils.version import LooseVersion
     124    return LooseVersion(out)>=LooseVersion(req_version)
     125
     126def is_RH():
     127    try:
     128        with open("/etc/redhat-release", mode='rb') as f:
     129            data = f.read()
     130        return data.startswith("CentOS") or data.startswith("RedHat")
     131    except:
     132        pass
     133    return False
     134
     135DEFAULT = True
     136if "--minimal" in sys.argv:
     137    sys.argv.remove("--minimal")
     138    DEFAULT = False
     139
     140from xpra.platform.features import LOCAL_SERVERS_SUPPORTED, SHADOW_SUPPORTED
     141shadow_ENABLED = SHADOW_SUPPORTED and DEFAULT
     142server_ENABLED = (LOCAL_SERVERS_SUPPORTED or shadow_ENABLED) and DEFAULT
     143rfb_ENABLED = server_ENABLED
     144service_ENABLED = LINUX and server_ENABLED
     145sd_listen_ENABLED = POSIX and pkg_config_ok("--exists", "libsystemd")
     146proxy_ENABLED  = DEFAULT
     147client_ENABLED = DEFAULT
     148scripts_ENABLED = not WIN32
     149cython_ENABLED = DEFAULT
     150modules_ENABLED = DEFAULT
     151data_ENABLED = DEFAULT
     152
     153x11_ENABLED = DEFAULT and not WIN32 and not OSX
     154xinput_ENABLED = x11_ENABLED
     155uinput_ENABLED = x11_ENABLED
     156dbus_ENABLED = DEFAULT and x11_ENABLED and not (OSX or WIN32)
     157gtk_x11_ENABLED = DEFAULT and not WIN32 and not OSX
     158gtk3_ENABLED = DEFAULT and client_ENABLED
     159opengl_ENABLED = DEFAULT and client_ENABLED
     160html5_ENABLED = DEFAULT
     161html5_gzip_ENABLED = DEFAULT
     162html5_brotli_ENABLED = DEFAULT
     163minify_ENABLED = html5_ENABLED
     164pam_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"))
     165
     166xdg_open_ENABLED        = (LINUX or FREEBSD) and DEFAULT
     167netdev_ENABLED          = LINUX and DEFAULT
     168vsock_ENABLED           = LINUX and os.path.exists("/usr/include/linux/vm_sockets.h")
     169bencode_ENABLED         = DEFAULT
     170cython_bencode_ENABLED  = DEFAULT
     171clipboard_ENABLED       = DEFAULT
     172Xdummy_ENABLED          = None if POSIX else False  #None means auto-detect
     173Xdummy_wrapper_ENABLED  = None if POSIX else False  #None means auto-detect
     174if WIN32 or OSX:
     175    Xdummy_ENABLED = False
     176sound_ENABLED           = DEFAULT
     177printing_ENABLED        = DEFAULT
     178crypto_ENABLED          = DEFAULT
     179mdns_ENABLED            = DEFAULT
     180websockets_ENABLED      = DEFAULT
     181
     182enc_proxy_ENABLED       = DEFAULT
     183enc_x264_ENABLED        = DEFAULT and pkg_config_ok("--exists", "x264")
     184#crashes on 32-bit windows:
     185enc_x265_ENABLED        = (not WIN32) and pkg_config_ok("--exists", "x265")
     186pillow_ENABLED          = DEFAULT
     187webp_ENABLED            = DEFAULT and pkg_config_version("0.5", "libwebp")
     188jpeg_encoder_ENABLED    = DEFAULT and pkg_config_version("1.2", "libturbojpeg")
     189jpeg_decoder_ENABLED    = DEFAULT and pkg_config_version("1.4", "libturbojpeg")
     190vpx_ENABLED             = DEFAULT and pkg_config_version("1.4", "vpx")
     191enc_ffmpeg_ENABLED      = DEFAULT and pkg_config_version("58.18", "libavcodec")
     192#opencv currently broken on 32-bit windows (crashes on load):
     193webcam_ENABLED          = DEFAULT and not OSX and (not WIN32 or BITS==64)
     194notifications_ENABLED   = DEFAULT
     195keyboard_ENABLED        = DEFAULT
     196v4l2_ENABLED            = DEFAULT and (not WIN32 and not OSX and not FREEBSD and not OPENBSD)
     197#ffmpeg 3.1 or later is required
     198dec_avcodec2_ENABLED    = DEFAULT and pkg_config_version("57", "libavcodec")
     199csc_swscale_ENABLED     = DEFAULT and pkg_config_ok("--exists", "libswscale")
     200csc_cython_ENABLED      = DEFAULT
     201nvenc_ENABLED = DEFAULT and BITS==64 and pkg_config_version("10", "nvenc")
     202nvfbc_ENABLED = DEFAULT and BITS==64 and pkg_config_ok("--exists", "nvfbc")
     203cuda_kernels_ENABLED    = DEFAULT
     204cuda_rebuild_ENABLED    = DEFAULT
     205csc_libyuv_ENABLED      = DEFAULT and pkg_config_ok("--exists", "libyuv")
     206example_ENABLED         = DEFAULT
     207
     208#Cython / gcc / packaging build options:
     209annotate_ENABLED        = DEFAULT
    86210warn_ENABLED            = True
    87211strict_ENABLED          = True
    88 PIC_ENABLED             = True
     212PIC_ENABLED             = not WIN32     #ming32 moans that it is always enabled already
    89213debug_ENABLED           = False
    90214verbose_ENABLED         = False
    91215bundle_tests_ENABLED    = False
     216tests_ENABLED           = False
     217rebuild_ENABLED         = "--skip-build" not in sys.argv
    92218
    93219#allow some of these flags to be modified on the command line:
    94 SWITCHES = ("enc_x264", "x264_static",
    95             "enc_x265", "x265_static",
    96             "nvenc",
    97             "dec_avcodec", "avcodec_static",
    98             "dec_avcodec2", "avcodec2_static",
    99             "csc_swscale", "swscale_static",
    100             "csc_nvcuda", "csc_opencl", "csc_cython",
    101             "vpx", "vpx_static",
    102             "webp", "webm",
    103             "rencode", "bencode", "cython_bencode",
    104             "clipboard",
    105             "server", "client", "x11",
    106             "gtk2", "gtk3", "qt4", "html5",
    107             "sound", "cyxor", "cymaths", "opengl", "argb",
    108             "warn", "strict", "shadow", "debug", "PIC", "Xdummy", "verbose", "bundle_tests")
     220SWITCHES = [
     221    "cython", "modules", "data",
     222    "enc_x264", "enc_x265", "enc_ffmpeg",
     223    "nvenc", "cuda_kernels", "cuda_rebuild", "nvfbc",
     224    "vpx", "webp", "pillow", "jpeg_encoder", "jpeg_decoder",
     225    "v4l2",
     226    "dec_avcodec2", "csc_swscale",
     227    "csc_cython", "csc_libyuv",
     228    "bencode", "cython_bencode", "vsock", "netdev", "mdns",
     229    "clipboard",
     230    "scripts",
     231    "server", "client", "dbus", "x11", "xinput", "uinput", "sd_listen",
     232    "gtk_x11", "service",
     233    "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",
     242    ]
    109243HELP = "-h" in sys.argv or "--help" in sys.argv
    110244if HELP:
     
    120254            default_str = "auto-detect"
    121255        print("%s or %s (default: %s)" % (with_str.ljust(25), without_str.ljust(30), default_str))
     256    print("  --pkg-config-path=PATH")
     257    print("  --rpath=PATH")
    122258    sys.exit(0)
    123259
     260install = None
     261rpath = None
     262ssl_cert = None
     263ssl_key = None
     264minifier = None
     265share_xpra = None
    124266filtered_args = []
    125267for arg in sys.argv:
    126     #deprecated flag:
    127     if arg == "--enable-Xdummy":
    128         Xdummy_ENABLED = True
     268    matched = False
     269    for x in ("rpath", "ssl-cert", "ssl-key", "install", "share-xpra"):
     270        varg = "--%s=" % x
     271        if arg.startswith(varg):
     272            value = arg[len(varg):]
     273            globals()[x.replace("-", "_")] = value
     274            #remove these arguments from sys.argv,
     275            #except for --install=PATH
     276            matched = x!="install"
     277            break
     278    if matched:
    129279        continue
    130     matched = False
    131280    for x in SWITCHES:
    132         if arg=="--with-%s" % x:
     281        with_str = "--with-%s" % x
     282        without_str = "--without-%s" % x
     283        if arg.startswith(with_str+"="):
     284            vars()["%s_ENABLED" % x] = arg[len(with_str)+1:]
     285            matched = True
     286            break
     287        elif arg==with_str:
    133288            vars()["%s_ENABLED" % x] = True
    134289            matched = True
    135290            break
    136         elif arg=="--without-%s" % x:
     291        elif arg==without_str:
    137292            vars()["%s_ENABLED" % x] = False
    138293            matched = True
     
    145300    for x in SWITCHES:
    146301        switches_info[x] = vars()["%s_ENABLED" % x]
    147     print("build switches: %s" % switches_info)
    148     if LOCAL_SERVERS_SUPPORTED:
    149         print("Xdummy build flag: %s" % Xdummy_ENABLED)
    150 
     302    print("build switches:")
     303    for k in sorted(SWITCHES):
     304        v = switches_info[k]
     305        print("* %s : %s" % (str(k).ljust(20), {None : "Auto", True : "Y", False : "N"}.get(v, v)))
     306
     307    if not cython_ENABLED:
     308        enc_ffmpeg_ENABLED = enc_x264_ENABLED = enc_x265_ENABLED = nvenc_ENABLED = False
     309        csc_swscale_ENABLED = csc_libyuv_ENABLED = csc_cython_ENABLED = False
     310        vpx_ENABLED = nvfbc_ENABLED = dec_avcodec2_ENABLED = False
     311        webp_ENABLED = jpeg_encoder_ENABLED = jpeg_decoder_ENABLED = False
     312        server_ENABLED = client_ENABLED = shadow_ENABLED = False
     313        cython_bencode_ENABLED = False
     314        gtk3_ENABLED = False
     315        x11_ENABLED = False
    151316    #sanity check the flags:
    152     if clipboard_ENABLED and not server_ENABLED and not gtk2_ENABLED and not gtk3_ENABLED:
     317    if clipboard_ENABLED and not server_ENABLED and not gtk3_ENABLED:
    153318        print("Warning: clipboard can only be used with the server or one of the gtk clients!")
    154319        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
    164320    if x11_ENABLED and WIN32:
    165321        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:
     322    if gtk_x11_ENABLED and not x11_ENABLED:
     323        print("Error: you must enable x11 to support gtk_x11!")
     324        exit(1)
     325    if client_ENABLED and not gtk3_ENABLED:
    167326        print("Warning: client is enabled but none of the client toolkits are!?")
    168     if not argb_ENABLED and (x11_ENABLED or OSX):
    169         print("Error: argb is required for x11 and osx builds!")
    170         exit(1)
    171     if not client_ENABLED and not server_ENABLED:
    172         print("Error: you must build at least the client or server!")
    173         exit(1)
    174 
     327    if DEFAULT and (not client_ENABLED and not server_ENABLED):
     328        print("Warning: you probably want to build at least the client or server!")
     329    if DEFAULT and not pillow_ENABLED:
     330        print("Warning: including Python Pillow is VERY STRONGLY recommended")
     331    if minify_ENABLED and WIN32:
     332        print("Warning: minifier is not supported on MS Windows")
     333        minify_ENABLED = False
     334    if html5_ENABLED and minify_ENABLED:
     335        r = get_status_output(["uglifyjs", "--version"])[0]
     336        if r==0:
     337            minifier = "uglifyjs"
     338        else:
     339            print("Warning: uglifyjs failed and return %i" % r)
     340            try:
     341                import yuicompressor
     342                assert yuicompressor
     343                minifier = "yuicompressor"
     344            except ImportError as e:
     345                print("Warning: yuicompressor module not found, cannot minify")
     346                minify_ENABLED = False
     347    if DEFAULT and (not enc_x264_ENABLED and not vpx_ENABLED):
     348        print("Warning: no x264 and no vpx support!")
     349        print(" you should enable at least one of these two video encodings")
     350
     351if install is None and WIN32:
     352    install = os.environ.get("MINGW_PREFIX", sys.prefix or "dist")
     353if share_xpra is None:
     354    share_xpra = os.path.join("share", "xpra")
    175355
    176356#*******************************************************************************
    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",
     357# default sets:
     358
     359external_includes = ["hashlib",
    204360                     "ctypes", "platform"]
     361
     362
     363if gtk3_ENABLED or sound_ENABLED:
     364    external_includes += ["gi"]
    205365
    206366external_excludes = [
    207367                    #Tcl/Tk
    208                     "Tkconstants", "Tkinter", "tcl",
     368                    "Tkconstants", "tkinter", "tcl",
    209369                    #PIL bits that import TK:
    210                     "_imagingtk", "PIL._imagingtk", "ImageTk", "PIL.ImageTk", "FixTk",
     370                    "PIL._tkinter_finder", "_imagingtk", "PIL._imagingtk", "ImageTk", "PIL.ImageTk", "FixTk",
    211371                    #formats we don't use:
    212372                    "GimpGradientFile", "GimpPaletteFile", "BmpImagePlugin", "TiffImagePlugin",
    213373                    #not used:
    214                     "curses", "email", "mimetypes", "mimetools", "pdb",
    215                     "urllib", "urllib2", "tty",
    216                     "ssl", "_ssl",
    217                     "cookielib", "BaseHTTPServer", "ftplib", "httplib", "fileinput",
    218                     "distutils", "setuptools", "doctest"
     374                    "curses", "pdb",
     375                    "tty",
     376                    "setuptools", "doctest"
     377                    "nose", "pytest", "_pytest", "pluggy", "more_itertools", "apipkg", "py", "funcsigs",
     378                    "Cython", "cython", "pyximport",
     379                    "pydoc_data",
    219380                    ]
    220 
     381if not html5_ENABLED and not crypto_ENABLED:
     382    external_excludes += ["ssl", "_ssl"]
     383if not html5_ENABLED:
     384    external_excludes += ["BaseHTTPServer"]
     385if not html5_ENABLED and not client_ENABLED:
     386    external_excludes += ["mimetools"]
     387
     388if not client_ENABLED and not server_ENABLED:
     389    excludes += ["PIL"]
     390if not dbus_ENABLED:
     391    excludes += ["dbus"]
    221392
    222393
    223394#because of differences in how we specify packages and modules
    224 #for distutils / py2app and py2exe
     395#for distutils / py2app and cx_freeze
    225396#use the following functions, which should get the right
    226397#data in the global variables "packages", "modules" and "excludes"
     
    253424
    254425def add_modules(*mods):
     426    def add(v):
     427        global modules
     428        if v not in modules:
     429            modules.append(v)
     430    do_add_modules(add, *mods)
     431
     432def do_add_modules(op, *mods):
    255433    """ adds the packages and any .py module found in the packages to the "modules" list
    256434    """
    257435    global modules
    258436    for x in mods:
    259         if x not in modules:
    260             modules.append(x)
     437        #ugly path stripping:
     438        if x.startswith("./"):
     439            x = x[2:]
     440        if x.endswith(".py"):
     441            x = x[:-3]
     442            x = x.replace("/", ".") #.replace("\\", ".")
    261443        pathname = os.path.sep.join(x.split("."))
     444        #is this a file module?
     445        f = "%s.py" % pathname
     446        if os.path.exists(f) and os.path.isfile(f):
     447            op(x)
    262448        if os.path.exists(pathname) and os.path.isdir(pathname):
    263449            #add all file modules found in this directory
    264450            for f in os.listdir(pathname):
    265                 if f.endswith(".py") and f.find("Copy ")<0:
     451                #make sure we only include python files,
     452                #and ignore eclipse copies
     453                if f.endswith(".py") and not f.startswith("Copy ")<0:
    266454                    fname = os.path.join(pathname, f)
    267455                    if os.path.isfile(fname):
    268456                        modname = "%s.%s" % (x, f.replace(".py", ""))
    269                         modules.append(modname)
     457                        op(modname)
    270458
    271459def toggle_packages(enabled, *module_names):
     
    275463        remove_packages(*module_names)
    276464
     465def toggle_modules(enabled, *module_names):
     466    if enabled:
     467        def op(v):
     468            global modules
     469            if v not in modules:
     470                modules.append(v)
     471        do_add_modules(op, *module_names)
     472    else:
     473        remove_packages(*module_names)
     474
     475
    277476#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 
     477if modules_ENABLED:
     478    add_modules("xpra", "xpra.platform", "xpra.net")
     479    add_modules("xpra.scripts.main")
     480
     481
     482def add_data_files(target_dir, files):
     483    #this is overriden below because cx_freeze uses the opposite structure (files first...). sigh.
     484    assert isinstance(target_dir, str)
     485    assert isinstance(files, (list, tuple))
     486    data_files.append((target_dir, files))
     487
     488
     489#for pretty printing of options:
     490def print_option(prefix, k, v):
     491    if isinstance(v, dict):
     492        print("%s* %s:" % (prefix, k))
     493        for kk,vv in v.items():
     494            print_option(" "+prefix, kk, vv)
     495    else:
     496        print("%s* %s=%s" % (prefix, k, v))
    284497
    285498#*******************************************************************************
    286499# Utility methods for building with Cython
     500def cython_version_compare(min_version):
     501    from distutils.version import LooseVersion
     502    assert cython_ENABLED
     503    from Cython.Compiler.Version import version as cython_version
     504    return LooseVersion(cython_version) >= LooseVersion(min_version)
     505
    287506def cython_version_check(min_version):
    288     try:
     507    if not cython_version_compare(min_version):
    289508        from Cython.Compiler.Version import version as cython_version
    290     except ImportError, e:
    291         sys.exit("ERROR: Cannot find Cython: %s" % e)
    292     from distutils.version import LooseVersion
    293     if LooseVersion(cython_version) < LooseVersion(".".join([str(x) for x in min_version])):
    294509        sys.exit("ERROR: Your version of Cython is too old to build this package\n"
    295510                 "You have version %s\n"
    296511                 "Please upgrade to Cython %s or better"
    297                  % (cython_version, ".".join([str(part) for part in min_version])))
    298 
    299 def cython_add(extension, min_version=(0, 14, 0)):
     512                 % (cython_version, min_version))
     513
     514def cython_add(extension, min_version="0.20"):
    300515    #gentoo does weird things, calls --no-compile with build *and* install
    301516    #then expects to find the cython modules!? ie:
    302     #python2.7 setup.py build -b build-2.7 install --no-compile --root=/var/tmp/portage/x11-wm/xpra-0.7.0/temp/images/2.7
     517    #python2.7 setup.py build -b build-2.7 install --no-compile \
     518    #    --root=/var/tmp/portage/x11-wm/xpra-0.7.0/temp/images/2.7
    303519    if "--no-compile" in sys.argv and not ("build" in sys.argv and "install" in sys.argv):
    304520        return
    305     global ext_modules, cmdclass
     521    assert cython_ENABLED, "cython compilation is disabled"
    306522    cython_version_check(min_version)
    307523    from Cython.Distutils import build_ext
    308524    ext_modules.append(extension)
    309     cmdclass = {'build_ext': build_ext}
     525    global cmdclass
     526    cmdclass['build_ext'] = build_ext
     527
     528def insert_into_keywords(kw, key, *args):
     529    values = kw.setdefault(key, [])
     530    for arg in args:
     531        values.insert(0, arg)
    310532
    311533def add_to_keywords(kw, key, *args):
     
    319541
    320542
     543def checkdirs(*dirs):
     544    for d in dirs:
     545        if not os.path.exists(d) or not os.path.isdir(d):
     546            raise Exception("cannot find a directory which is required for building: '%s'" % d)
     547
    321548PYGTK_PACKAGES = ["pygobject-2.0", "pygtk-2.0"]
    322549
     
    324551def get_gcc_version():
    325552    global GCC_VERSION
    326     if len(GCC_VERSION)==0:
    327         cmd = [os.environ.get("CC", "gcc"), "-v"]
    328         proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    329         output, _ = proc.communicate()
    330         status = proc.wait()
    331         if status==0:
     553    if not GCC_VERSION:
     554        cc = os.environ.get("CC", "gcc")
     555        r, _, err = get_status_output([cc]+["-v"])
     556        if r==0:
    332557            V_LINE = "gcc version "
    333             for line in output.decode("utf8").splitlines():
     558            for line in err.splitlines():
    334559                if line.startswith(V_LINE):
    335560                    v_str = line[len(V_LINE):].split(" ")[0]
     
    337562                        try:
    338563                            GCC_VERSION.append(int(p))
    339                         except:
     564                        except ValueError:
    340565                            break
    341566                    print("found gcc version: %s" % ".".join([str(x) for x in GCC_VERSION]))
     
    343568    return GCC_VERSION
    344569
    345 def make_constants_pxi(constants_path, pxi_path):
    346     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):
    384     base = os.path.join(os.getcwd(), *paths)
    385     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"
    394     if reason:
    395         if verbose_ENABLED:
    396             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
     570
     571def should_rebuild(src_file, bin_file):
     572    if not os.path.exists(bin_file):
     573        return "no file"
     574    if rebuild_ENABLED:
     575        if os.path.getctime(bin_file)<os.path.getctime(src_file):
     576            return "binary file out of date"
     577        if os.path.getctime(bin_file)<os.path.getctime(__file__):
     578            return "newer build file"
     579    return None
     580
    414581
    415582# 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 
     583def exec_pkgconfig(*pkgs_options, **ekw):
    423584    kw = dict(ekw)
    424     if len(pkgs_options)>0:
     585    optimize = kw.pop("optimize", None)
     586    if optimize and not debug_ENABLED:
     587        if isinstance(optimize, bool):
     588            optimize = int(optimize)*3
     589        add_to_keywords(kw, 'extra_compile_args', "-O%i" % optimize)
     590    ignored_flags = kw.pop("ignored_flags", [])
     591    ignored_tokens = kw.pop("ignored_tokens", [])
     592
     593    #for distros that don't patch distutils,
     594    #we have to add the python cflags:
     595    if not (is_Fedora() or is_Debian() or is_CentOS() or is_RedHat()):
     596        import shlex
     597        import sysconfig
     598        for cflag in shlex.split(sysconfig.get_config_var('CFLAGS') or ''):
     599            add_to_keywords(kw, 'extra_compile_args', cflag)
     600
     601    def add_tokens(s, extra="extra_link_args", extra_map={"-W" : "extra_compile_args"}):
     602        if not s:
     603            return
     604        flag_map = {'-I': 'include_dirs',
     605                    '-L': 'library_dirs',
     606                    '-l': 'libraries'}
     607        for token in s.split():
     608            if token in ignored_tokens:
     609                pass
     610            elif token[:2] in ignored_flags:
     611                pass
     612            elif token[:2] in flag_map:
     613                if len(token)>2:
     614                    add_to_keywords(kw, flag_map.get(token[:2]), token[2:])
     615                else:
     616                    print("Warning: invalid token '%s'" % token)
     617            else:
     618                extra_name = extra_map.get(token, extra)
     619                add_to_keywords(kw, extra_name, token)
     620
     621    if pkgs_options:
    425622        package_names = []
    426623        #find out which package name to use from potentially many options
     
    429626            #for this package options, find the ones that work
    430627            valid_option = None
    431             if type(package_options)==str:
     628            if isinstance(package_options, str):
    432629                options = [package_options]     #got given just one string
    433                 if not package_options.startswith("lib"):
    434                     options.append("lib%s" % package_options)
    435630            else:
    436                 assert type(package_options)==list
     631                assert isinstance(package_options, list)
    437632                options = package_options       #got given a list of options
    438633            for option in options:
    439634                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:
     635                r = get_status_output(cmd)[0]
     636                if r==0:
    443637                    valid_option = option
    444638                    break
    445639            if not valid_option:
    446                 sys.exit("ERROR: cannot find a valid pkg-config package for %s" % (options,))
     640                raise Exception("ERROR: cannot find a valid pkg-config entry for %s using PKG_CONFIG_PATH=%s" %
     641                                (" or ".join(options), os.environ.get("PKG_CONFIG_PATH", "(empty)")))
    447642            package_names.append(valid_option)
    448643        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))
     644            print("exec_pkgconfig(%s,%s) using package names=%s" % (pkgs_options, ekw, package_names))
     645        pkg_config_cmd = ["pkg-config", "--libs", "--cflags", "%s" % (" ".join(package_names),)]
     646        r, pkg_config_out, err = get_status_output(pkg_config_cmd)
     647        if r!=0:
     648            sys.exit("ERROR: call to '%s' failed (err=%s)" % (" ".join(cmd), err))
     649        add_tokens(pkg_config_out)
    468650    if warn_ENABLED:
    469651        add_to_keywords(kw, 'extra_compile_args', "-Wall")
    470652        add_to_keywords(kw, 'extra_link_args', "-Wall")
    471653    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"
     654        if os.environ.get("CC", "").find("clang")>=0:
     655            #clang emits too many warnings with cython code,
     656            #so we can't enable Werror without turning off some warnings:
     657            #this list of flags should allow clang to build the whole source tree,
     658            #as of Cython 0.26 + clang 4.0. Other version combinations may require
     659            #(un)commenting other switches.
     660            eifd = ["-Werror",
     661                    #"-Wno-unneeded-internal-declaration",
     662                    #"-Wno-unknown-attributes",
     663                    #"-Wno-unused-function",
     664                    #"-Wno-self-assign",
     665                    #"-Wno-sometimes-uninitialized",
     666                    #cython adds rpath to the compilation command??
     667                    #and the "-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1" is also ignored by clang:
     668                    "-Wno-deprecated-register",
     669                    "-Wno-unused-command-line-argument",
     670                    ]
     671        elif get_gcc_version()>=[4, 4]:
     672            eifd = ["-Werror"]
     673            if NETBSD:
     674                #see: http://trac.cython.org/ticket/395
     675                eifd += ["-fno-strict-aliasing"]
     676            elif FREEBSD:
     677                eifd += ["-Wno-error=unused-function"]
    475678        else:
    476             eifd = "-Werror-implicit-function-declaration"
    477         add_to_keywords(kw, 'extra_compile_args', eifd)
     679            #older versions of OSX ship an old gcc,
     680            #not much we can do with this:
     681            eifd = []
     682        for eif in eifd:
     683            add_to_keywords(kw, 'extra_compile_args', eif)
     684    if sys.version_info>=(3,7):
     685        #we'll switch to the "new" buffer interface eventually
     686        #until then, silence those deprecation warnings:
     687        add_to_keywords(kw, 'extra_compile_args', "-Wno-error=deprecated-declarations")
    478688    if PIC_ENABLED:
    479689        add_to_keywords(kw, 'extra_compile_args', "-fPIC")
     
    481691        add_to_keywords(kw, 'extra_compile_args', '-g')
    482692        add_to_keywords(kw, 'extra_compile_args', '-ggdb')
    483         kw['cython_gdb'] = True
    484         if get_gcc_version()>=4.8:
     693        if get_gcc_version()>=[4, 8] and not WIN32:
    485694            add_to_keywords(kw, 'extra_compile_args', '-fsanitize=address')
    486695            add_to_keywords(kw, 'extra_link_args', '-fsanitize=address')
     696    if rpath and kw.get("libraries"):
     697        insert_into_keywords(kw, "library_dirs", rpath)
     698        insert_into_keywords(kw, "extra_link_args", "-Wl,-rpath=%s" % rpath)
     699    add_tokens(os.environ.get("CFLAGS"), "extra_compile_args", {})
     700    add_tokens(os.environ.get("LDFLAGS"), "extra_link_args", {})
    487701    #add_to_keywords(kw, 'include_dirs', '.')
    488702    if verbose_ENABLED:
    489         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
     703        print("exec_pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    490704    return kw
     705pkgconfig = exec_pkgconfig
    491706
    492707
    493708#*******************************************************************************
    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()
    533     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()
     709
     710
     711def get_base_conf_dir(install_dir, stripbuildroot=True):
     712    #in some cases we want to strip the buildroot (to generate paths in the config file)
     713    #but in other cases we want the buildroot path (when writing out the config files)
     714    #and in some cases, we don't have the install_dir specified (called from detect_xorg_setup, and that's fine too)
     715    #this is a bit hackish, but I can't think of a better way of detecting it
     716    #(ie: "$HOME/rpmbuild/BUILDROOT/xpra-0.15.0-0.fc21.x86_64/usr")
     717    dirs = (install_dir or sys.prefix).split(os.path.sep)
     718    if install_dir and stripbuildroot:
     719        pkgdir = os.environ.get("pkgdir")
     720        if "debian" in dirs and "tmp" in dirs:
     721            #ugly fix for stripping the debian tmp dir:
     722            #ie: "???/tmp/???/tags/v0.15.x/src/debian/tmp/" -> ""
     723            while "tmp" in dirs:
     724                dirs = dirs[dirs.index("tmp")+1:]
     725        elif "debian" in dirs:
     726            #same for recent debian versions:
     727            #ie: "xpra-2.0.2/debian/xpra/usr" -> "usr"
     728            i = dirs.index("debian")
     729            while i>=0 and len(dirs)>i+1:
     730                if dirs[i+1] == "xpra":
     731                    dirs = dirs[i+2:]
     732                i = dirs.index("debian")
     733        elif "BUILDROOT" in dirs:
     734            #strip rpm style build root:
     735            #[$HOME, "rpmbuild", "BUILDROOT", "xpra-$VERSION"] -> []
     736            dirs = dirs[dirs.index("BUILDROOT")+2:]
     737        elif "pkg" in dirs:
     738            #archlinux
     739            #ie: "/build/xpra/pkg/xpra/etc" -> "etc"
     740            #find the last 'pkg' from the list of directories:
     741            i = max(loc for loc, val in enumerate(dirs) if val == "pkg")
     742            if len(dirs)>i+1 and dirs[i+1] in ("xpra", "xpra-svn"):
     743                dirs = dirs[i+2:]
     744        elif pkgdir and install_dir.startswith(pkgdir):
     745            #arch build dir:
     746            dirs = install_dir.lstrip(pkgdir).split(os.path.sep)
     747        elif "usr" in dirs:
     748            #ie: ["some", "path", "to", "usr"] -> ["usr"]
     749            #assume "/usr" or "/usr/local" is the build root
     750            while "usr" in dirs[1:]:
     751                dirs = dirs[dirs[1:].index("usr")+1:]
     752        elif "image" in dirs:
     753            # Gentoo's "${PORTAGE_TMPDIR}/portage/${CATEGORY}/${PF}/image/_python2.7" -> ""
     754            while "image" in dirs:
     755                dirs = dirs[dirs.index("image")+2:]
     756    #now deal with the fact that "/etc" is used for the "/usr" prefix
     757    #but "/usr/local/etc" is used for the "/usr/local" prefix..
     758    if dirs and dirs[-1]=="usr":
     759        dirs = dirs[:-1]
     760    #is this an absolute path?
     761    if not dirs or dirs[0]=="usr" or (install_dir or sys.prefix).startswith(os.path.sep):
     762        #ie: ["/", "usr"] or ["/", "usr", "local"]
     763        dirs.insert(0, os.path.sep)
     764    return dirs
     765
     766def get_conf_dir(install_dir, stripbuildroot=True):
     767    dirs = get_base_conf_dir(install_dir, stripbuildroot)
     768    if "etc" not in dirs:
     769        dirs.append("etc")
     770    dirs.append("xpra")
     771    return os.path.join(*dirs)
     772
     773def detect_xorg_setup(install_dir=None):
     774    from xpra.scripts import config
     775    config.debug = config.warn
     776    conf_dir = get_conf_dir(install_dir)
     777    return config.detect_xvfb_command(conf_dir, None, Xdummy_ENABLED, Xdummy_wrapper_ENABLED)
     778
     779def build_xpra_conf(install_dir):
     780    #generates an actual config file from the template
     781    xvfb_command = detect_xorg_setup(install_dir)
     782    fake_xinerama = "no"
     783    if POSIX and not OSX and not (is_Debian() or is_Ubuntu()):
     784        from xpra.x11.fakeXinerama import find_libfakeXinerama
     785        fake_xinerama = find_libfakeXinerama() or "auto"
     786    from xpra.platform.features import DEFAULT_ENV
     787    def bstr(b):
     788        if b is None:
     789            return "auto"
     790        return "yes" if int(b) else "no"
     791    start_env = "\n".join("start-env = %s" % x for x in DEFAULT_ENV)
     792    conf_dir = get_conf_dir(install_dir)
     793    from xpra.platform.features import DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS
     794    from xpra.platform.paths import get_socket_dirs
     795    from xpra.scripts.config import (
     796        xvfb_cmd_str,
     797        get_default_key_shortcuts, get_default_systemd_run, get_default_pulseaudio_command,
     798        DEFAULT_POSTSCRIPT_PRINTER, DEFAULT_PULSEAUDIO,
     799        )
     800    #remove build paths and user specific paths with UID ("/run/user/UID/Xpra"):
     801    socket_dirs = get_socket_dirs()
     802    if POSIX and os.getuid()>0:
     803        #remove any paths containing the uid,
     804        #osx uses /var/tmp/$UID-Xpra,
     805        #but this should not be included in the default config for all users!
     806        #(the buildbot's uid!)
     807        socket_dirs = [x for x in socket_dirs if x.find(str(os.getuid()))<0]
     808    #FIXME: we should probably get these values from the default config instead
     809    pdf, postscript = "", ""
     810    if POSIX and printing_ENABLED:
     811        try:
     812            if "/usr/sbin" not in sys.path:
     813                sys.path.append("/usr/sbin")
     814            from xpra.platform.pycups_printing import get_printer_definition
     815            print("probing cups printer definitions")
     816            pdf = get_printer_definition("pdf")
     817            postscript = get_printer_definition("postscript") or DEFAULT_POSTSCRIPT_PRINTER
     818            print("pdf=%s, postscript=%s" % (pdf, postscript))
     819        except Exception as e:
     820            print("could not probe for pdf/postscript printers: %s" % e)
     821    def pretty_cmd(cmd):
     822        return " ".join(cmd)
     823    #OSX doesn't have webcam support yet (no opencv builds on 10.5.x)
     824    webcam = webcam_ENABLED and not (OSX or WIN32)
     825    #no python-avahi on RH / CentOS, need dbus module on *nix:
     826    mdns = mdns_ENABLED and (OSX or WIN32 or (not is_RH() and dbus_ENABLED))
     827    SUBS = {
     828            'xvfb_command'          : xvfb_cmd_str(xvfb_command),
     829            'fake_xinerama'         : fake_xinerama,
     830            'ssh_command'           : "auto",
     831            'key_shortcuts'         : "".join(("key-shortcut = %s\n" % x) for x in get_default_key_shortcuts()),
     832            'remote_logging'        : "both",
     833            'start_env'             : start_env,
     834            'pulseaudio'            : bstr(DEFAULT_PULSEAUDIO),
     835            'pulseaudio_command'    : pretty_cmd(get_default_pulseaudio_command()),
     836            'pulseaudio_configure_commands' : "\n".join(("pulseaudio-configure-commands = %s" % pretty_cmd(x)) for x in DEFAULT_PULSEAUDIO_CONFIGURE_COMMANDS),
     837            'conf_dir'              : conf_dir,
     838            'bind'                  : "auto",
     839            'ssl_cert'              : ssl_cert or "",
     840            'ssl_key'               : ssl_key or "",
     841            'systemd_run'           : get_default_systemd_run(),
     842            'socket_dirs'           : "".join(("socket-dirs = %s\n" % x) for x in socket_dirs),
     843            'log_dir'               : "auto",
     844            'mdns'                  : bstr(mdns),
     845            'notifications'         : bstr(OSX or WIN32 or dbus_ENABLED),
     846            'dbus_proxy'            : bstr(not OSX and not WIN32 and dbus_ENABLED),
     847            'pdf_printer'           : pdf,
     848            'postscript_printer'    : postscript,
     849            'webcam'                : ["no", "auto"][webcam],
     850            'mousewheel'            : "on",
     851            'printing'              : bstr(printing_ENABLED),
     852            'dbus_control'          : bstr(dbus_ENABLED),
     853            'mmap'                  : bstr(True),
     854            'opengl'                : "probe",
     855            'headerbar'             : ["yes", "no"][OSX],
     856            }
     857    def convert_templates(subdirs):
     858        dirname = os.path.join(*(["etc", "xpra"] + subdirs))
     859        #get conf dir for install, without stripping the build root
     860        target_dir = os.path.join(get_conf_dir(install_dir, stripbuildroot=False), *subdirs)
     861        print("convert_templates(%s) dirname=%s, target_dir=%s" % (subdirs, dirname, target_dir))
     862        if not os.path.exists(target_dir):
     863            try:
     864                os.makedirs(target_dir)
     865            except Exception as e:
     866                print("cannot create target dir '%s': %s" % (target_dir, e))
     867        for f in sorted(os.listdir(dirname)):
     868            if f.endswith("osx.conf.in") and not OSX:
     869                continue
     870            filename = os.path.join(dirname, f)
     871            if os.path.isdir(filename):
     872                convert_templates(subdirs+[f])
     873                continue
     874            if not f.endswith(".in"):
     875                continue
     876            with open(filename, "r") as f_in:
     877                template  = f_in.read()
     878            target_file = os.path.join(target_dir, f[:-len(".in")])
     879            print("generating %s from %s" % (target_file, f))
     880            with open(target_file, "w") as f_out:
     881                config_data = template % SUBS
     882                f_out.write(config_data)
     883    convert_templates([])
    576884
    577885
    578886#*******************************************************************************
    579 if 'clean' in sys.argv or 'sdist' in sys.argv:
     887def clean():
    580888    #clean and sdist don't actually use cython,
    581889    #so skip this (and avoid errors)
    582     def pkgconfig(*pkgs_options, **ekw):
    583         return {}
     890    global pkgconfig
     891    pkgconfig = no_pkgconfig
    584892    #always include everything in this case:
    585893    add_packages("xpra")
    586894    #ensure we remove the files we generate:
    587895    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",
    592                    "xpra/x11/bindings/constants.pxi",
     896                   "xpra/build_info.py",
     897                   "xpra/monotonic_time.c",
     898                   "xpra/gtk_common/gtk3/gdk_atoms.c",
     899                   "xpra/gtk_common/gtk3/gdk_bindings.c",
     900                   "xpra/x11/gtk3/gdk_bindings.c",
     901                   "xpra/x11/gtk3/gdk_display_source.c",
    593902                   "xpra/x11/bindings/wait_for_x_server.c",
    594903                   "xpra/x11/bindings/keyboard_bindings.c",
     
    597906                   "xpra/x11/bindings/randr_bindings.c",
    598907                   "xpra/x11/bindings/core_bindings.c",
     908                   "xpra/x11/bindings/posix_display_source.c",
    599909                   "xpra/x11/bindings/ximage.c",
    600                    "xpra/net/rencode/rencode.c",
     910                   "xpra/x11/bindings/xi2_bindings.c",
     911                   "xpra/platform/win32/propsys.cpp",
     912                   "xpra/platform/darwin/gdk_bindings.c",
     913                   "xpra/platform/xposix/sd_listen.c",
     914                   "xpra/platform/xposix/netdev_query.c",
     915                   "xpra/net/bencode/cython_bencode.c",
     916                   "xpra/net/vsock.c",
     917                   "xpra/buffers/membuf.c",
    601918                   "xpra/codecs/vpx/encoder.c",
    602919                   "xpra/codecs/vpx/decoder.c",
    603920                   "xpra/codecs/nvenc/encoder.c",
    604                    "xpra/codecs/nvenc/constants.pxi",
     921                   "xpra/codecs/nvfbc/fbc_capture_linux.cpp",
     922                   "xpra/codecs/nvfbc/fbc_capture_win.cpp",
    605923                   "xpra/codecs/enc_x264/encoder.c",
    606924                   "xpra/codecs/enc_x265/encoder.c",
    607                    "xpra/codecs/webp/encode.c",
    608                    "xpra/codecs/dec_avcodec/decoder.c",
    609                    "xpra/codecs/dec_avcodec/constants.pxi",
     925                   "xpra/codecs/jpeg/encoder.c",
     926                   "xpra/codecs/jpeg/decoder.c",
     927                   "xpra/codecs/enc_ffmpeg/encoder.c",
     928                   "xpra/codecs/v4l2/pusher.c",
     929                   "xpra/codecs/v4l2/constants.pxi",
     930                   "xpra/codecs/libav_common/av_log.c",
     931                   "xpra/codecs/webp/encoder.c",
     932                   "xpra/codecs/webp/decoder.c",
    610933                   "xpra/codecs/dec_avcodec2/decoder.c",
     934                   "xpra/codecs/csc_libyuv/colorspace_converter.cpp",
    611935                   "xpra/codecs/csc_swscale/colorspace_converter.c",
    612                    "xpra/codecs/csc_swscale/constants.pxi",
    613936                   "xpra/codecs/csc_cython/colorspace_converter.c",
    614937                   "xpra/codecs/xor/cyxor.c",
    615938                   "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")
     939                   "xpra/codecs/nvapi_version.c",
     940                   "xpra/gtk_common/gdk_atoms.c",
     941                   "xpra/client/gtk3/cairo_workaround.c",
     942                   "xpra/server/cystats.c",
     943                   "xpra/rectangle.c",
     944                   "xpra/server/window/motion.c",
     945                   "xpra/server/pam.c",
     946                   "etc/xpra/xpra.conf",
     947                   #special case for the generated xpra conf files in build (see #891):
     948                   "build/etc/xpra/xpra.conf"] + glob.glob("build/etc/xpra/conf.d/*.conf")
     949    if cuda_rebuild_ENABLED:
     950        CLEAN_FILES += [
     951            "xpra/codecs/cuda_common/ARGB_to_NV12.fatbin",
     952            "xpra/codecs/cuda_common/ARGB_to_YUV444.fatbin",
     953            "xpra/codecs/cuda_common/BGRA_to_NV12.fatbin",
     954            "xpra/codecs/cuda_common/BGRA_to_YUV444.fatbin",
     955            ]
     956    for x in CLEAN_FILES:
     957        p, ext = os.path.splitext(x)
     958        if ext in (".c", ".cpp", ".pxi"):
     959            #clean the Cython annotated html files:
     960            CLEAN_FILES.append(p+".html")
     961            if WIN32:
     962                #on win32, the build creates ".pyd" files, clean those too:
     963                CLEAN_FILES.append(p+".pyd")
     964                #when building with python3, we need to clean files named like:
     965                #"xpra/codecs/csc_libyuv/colorspace_converter-cpython-36m.dll"
     966                filename = os.path.join(os.getcwd(), p.replace("/", os.path.sep)+"*.dll")
     967                CLEAN_FILES += glob.glob(filename)
    623968    if 'clean' in sys.argv:
    624969        CLEAN_FILES.append("xpra/build_info.py")
     
    630975            os.unlink(filename)
    631976
    632 from add_build_info import record_build_info, record_src_info, has_src_info
     977if 'clean' in sys.argv or 'sdist' in sys.argv:
     978    clean()
     979
     980from add_build_info import record_build_info, BUILD_INFO_FILE, record_src_info, SRC_INFO_FILE, has_src_info
    633981
    634982if "clean" not in sys.argv:
    635983    # Add build info to build_info.py file:
    636984    record_build_info()
     985    if modules_ENABLED:
     986        # ensure it is included in the module list if it didn't exist before
     987        add_modules(BUILD_INFO_FILE)
    637988
    638989if "sdist" in sys.argv:
    639990    record_src_info()
    640991
    641 if "install" in sys.argv:
     992if "install" in sys.argv or "build" in sys.argv:
    642993    #if installing from source tree rather than
    643994    #from a source snapshot, we may not have a "src_info" file
    644995    #so create one:
    645     if not has_src_info():
     996    if not has_src_info() and modules_ENABLED:
    646997        record_src_info()
     998        # ensure it is now included in the module list
     999        add_modules(SRC_INFO_FILE)
    6471000
    6481001
     
    6591012        for f in files:
    6601013            dirname = root[len(srcdir)+1:]
    661             filename = os.path.join(root, f)
    662             m.setdefault(dirname, []).append(filename)
     1014            m.setdefault(dirname, []).append(os.path.join(root, f))
    6631015    return m
     1016
     1017
     1018def install_html5(install_dir="www"):
     1019    from setup_html5 import install_html5 as do_install_html5
     1020    do_install_html5(install_dir, minifier, html5_gzip_ENABLED, html5_brotli_ENABLED, verbose_ENABLED)
     1021
    6641022
    6651023#*******************************************************************************
    6661024if WIN32:
    667     # The Microsoft C library DLLs:
    668     # Unfortunately, these files cannot be re-distributed legally :(
    669     # So here is the md5sum so you can find the right version:
    670     # (you can find them in various packages, including Visual Studio 2008,
    671     # pywin32, etc...)
    672     import md5
    673     md5sums = {"Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest" : "37f44d535dcc8bf7a826dfa4f5fa319b",
    674                "Microsoft.VC90.CRT/msvcm90.dll"                 : "4a8bc195abdc93f0db5dab7f5093c52f",
    675                "Microsoft.VC90.CRT/msvcp90.dll"                 : "6de5c66e434a9c1729575763d891c6c2",
    676                "Microsoft.VC90.CRT/msvcr90.dll"                 : "e7d91d008fe76423962b91c43c88e4eb",
    677                "Microsoft.VC90.CRT/vcomp90.dll"                 : "f6a85f3b0e30c96c993c69da6da6079e",
    678                "Microsoft.VC90.MFC/Microsoft.VC90.MFC.manifest" : "17683bda76942b55361049b226324be9",
    679                "Microsoft.VC90.MFC/mfc90.dll"                   : "462ddcc5eb88f34aed991416f8e354b2",
    680                "Microsoft.VC90.MFC/mfc90u.dll"                  : "b9030d821e099c79de1c9125b790e2da",
    681                "Microsoft.VC90.MFC/mfcm90.dll"                  : "d4e7c1546cf3131b7d84b39f8da9e321",
    682                "Microsoft.VC90.MFC/mfcm90u.dll"                 : "371226b8346f29011137c7aa9e93f2f6",
    683                }
    684     # This is where I keep them, you will obviously need to change this value:
    685     C_DLLs = "C:\\"
    686     for dll_file, md5sum in md5sums.items():
    687         filename = os.path.join(C_DLLs, *dll_file.split("/"))
    688         if not os.path.exists(filename) or not os.path.isfile(filename):
    689             sys.exit("ERROR: DLL file %s is missing or not a file!" % filename)
    690         sys.stdout.write("* verifying md5sum for %s: " % filename)
    691         f = open(filename, mode='rb')
    692         data = f.read()
    693         f.close()
    694         m = md5.new()
    695         m.update(data)
    696         digest = m.hexdigest()
    697         assert digest==md5sum, "md5 digest for file %s does not match, expected %s but found %s" % (dll_file, md5sum, digest)
    698         sys.stdout.write("OK\n")
    699         sys.stdout.flush()
    700     #this should all be done with pkgconfig...
    701     #but until someone figures this out, the ugly path code below works
    702     #as long as you install in the same place or tweak the paths.
    703 
    704     #first some header crap so codecs can find the inttypes.h
    705     #and stdint.h:
    706     win32_include_dir = os.path.join(os.getcwd(), "win32")
    707 
    708     #cuda:
    709     cuda_path = "C:\\NVIDIA\CUDA\CUDAToolkit"
    710     cuda_include_dir   = os.path.join(cuda_path, "include")
    711     cuda_bin_dir       = os.path.join(cuda_path, "bin")
    712 
    713     #ffmpeg is needed for both swscale and x264:
    714     libffmpeg_path = None
    715     if dec_avcodec_ENABLED:
    716         assert not dec_avcodec2_ENABLED, "cannot enable both dec_avcodec and dec_avcodec2"
    717         libffmpeg_path = "C:\\ffmpeg-win32-bin"
    718     elif dec_avcodec2_ENABLED:
    719         assert not dec_avcodec_ENABLED, "cannot enable both dec_avcodec and dec_avcodec2"
    720         libffmpeg_path = "C:\\ffmpeg2-win32-bin"
    721     else:
    722         if csc_swscale_ENABLED:
    723             for p in ("C:\\ffmpeg2-win32-bin", "C:\\ffmpeg-win32-bin"):
    724                 if os.path.exists(p):
    725                     libffmpeg_path = p
    726             assert libffmpeg_path is not None, "no ffmpeg found, cannot use csc_swscale"
    727     libffmpeg_include_dir   = os.path.join(libffmpeg_path, "include")
    728     libffmpeg_lib_dir       = os.path.join(libffmpeg_path, "lib")
    729     libffmpeg_bin_dir       = os.path.join(libffmpeg_path, "bin")
    730     #x265
    731     x265_path ="C:\\x265"
    732     x265_include_dir    = x265_path
    733     x265_lib_dir        = x265_path
    734     x265_bin_dir        = x265_path
    735     #x264 (direct from build dir.. yuk - sorry!):
    736     x264_path ="C:\\x264"
    737     x264_include_dir    = x264_path
    738     x264_lib_dir        = x264_path
    739     x264_bin_dir        = x264_path
    740     # Same for vpx:
    741     # http://code.google.com/p/webm/downloads/list
    742     #the path after installing may look like this:
    743     #vpx_PATH="C:\\vpx-vp8-debug-src-x86-win32mt-vs9-v1.1.0"
    744     #but we use something more generic, without the version numbers:
    745     vpx_path = ""
    746     for p in ("C:\\vpx-1.3", "C:\\vpx-1.2", "C:\\vpx-1.1", "C:\\vpx-vp8"):
    747         if os.path.exists(p) and os.path.isdir(p):
    748             vpx_path = p
    749             break
    750     vpx_include_dir     = os.path.join(vpx_path, "include")
    751     vpx_lib_dir         = os.path.join(vpx_path, "lib", "Win32")
    752     if os.path.exists(os.path.join(vpx_lib_dir, "vpx.lib")):
    753         vpx_lib_names = ["vpx"]               #for libvpx 1.3.0
    754     elif os.path.exists(os.path.join(vpx_lib_dir, "vpxmd.lib")):
    755         vpx_lib_names = ["vpxmd"]             #for libvpx 1.2.0
    756     else:
    757         vpx_lib_names = ["vpxmt", "vpxmtd"]   #for libvpx 1.1.0
    758     #webp:
    759     webp_path = "C:\\libwebp-windows-x86"
    760     webp_include_dir    = webp_path+"\\include"
    761     webp_lib_dir        = webp_path+"\\lib"
    762     webp_bin_dir        = webp_path+"\\bin"
    763     webp_lib_names      = ["libwebp"]
    764 
    765     # Same for PyGTK:
    766     # http://www.pygtk.org/downloads.html
    767     gtk2_path = "C:\\Python27\\Lib\\site-packages\\gtk-2.0"
    768     python_include_path = "C:\\Python27\\include"
    769     gtk2runtime_path        = os.path.join(gtk2_path, "runtime")
    770     gtk2_lib_dir            = os.path.join(gtk2runtime_path, "bin")
    771     gtk2_base_include_dir   = os.path.join(gtk2runtime_path, "include")
    772 
    773     pygtk_include_dir       = os.path.join(python_include_path, "pygtk-2.0")
    774     atk_include_dir         = os.path.join(gtk2_base_include_dir, "atk-1.0")
    775     gtk2_include_dir        = os.path.join(gtk2_base_include_dir, "gtk-2.0")
    776     gdkpixbuf_include_dir   = os.path.join(gtk2_base_include_dir, "gdk-pixbuf-2.0")
    777     glib_include_dir        = os.path.join(gtk2_base_include_dir, "glib-2.0")
    778     cairo_include_dir       = os.path.join(gtk2_base_include_dir, "cairo")
    779     pango_include_dir       = os.path.join(gtk2_base_include_dir, "pango-1.0")
    780     gdkconfig_include_dir   = os.path.join(gtk2runtime_path, "lib", "gtk-2.0", "include")
    781     glibconfig_include_dir  = os.path.join(gtk2runtime_path, "lib", "glib-2.0", "include")
    782 
    783     def checkdirs(*dirs):
    784         for d in dirs:
    785             if not os.path.exists(d) or not os.path.isdir(d):
    786                 raise Exception("cannot find a directory which is required for building: %s" % d)
    787 
    788     def pkgconfig(*pkgs_options, **ekw):
    789         kw = dict(ekw)
    790         #remove static flag on win32..
    791         static = kw.get("static", None)
    792         if static is not None:
    793             del kw["static"]
    794         #always add the win32 include dirs, everyone needs that:
    795         add_to_keywords(kw, 'include_dirs', win32_include_dir)
    796         if len(pkgs_options)==0:
    797             return kw
    798 
    799         def add_to_PATH(bindir):
    800             if os.environ['PATH'].find(bindir)<0:
    801                 os.environ['PATH'] = bindir + ';' + os.environ['PATH']
    802             if bindir not in sys.path:
    803                 sys.path.append(bindir)
    804         if "avcodec" in pkgs_options[0]:
    805             add_to_PATH(libffmpeg_bin_dir)
    806             add_to_keywords(kw, 'include_dirs', libffmpeg_include_dir)
    807             add_to_keywords(kw, 'libraries', "avcodec", "avutil")
    808             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_lib_dir)
    809             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_bin_dir)
    810             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    811             checkdirs(libffmpeg_include_dir, libffmpeg_lib_dir, libffmpeg_bin_dir)
    812         elif "swscale" in pkgs_options[0]:
    813             add_to_PATH(libffmpeg_bin_dir)
    814             add_to_keywords(kw, 'include_dirs', libffmpeg_include_dir)
    815             add_to_keywords(kw, 'libraries', "swscale", "avutil")
    816             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_lib_dir)
    817             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % libffmpeg_bin_dir)
    818             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    819             checkdirs(libffmpeg_include_dir, libffmpeg_lib_dir, libffmpeg_bin_dir)
    820         elif "x264" in pkgs_options[0]:
    821             add_to_PATH(libffmpeg_bin_dir)
    822             add_to_PATH(x264_bin_dir)
    823             add_to_keywords(kw, 'include_dirs', x264_include_dir)
    824             add_to_keywords(kw, 'libraries', "libx264")
    825             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % x264_lib_dir)
    826             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    827             checkdirs(x264_include_dir, x264_lib_dir)
    828         elif "x265" in pkgs_options[0]:
    829             add_to_PATH(libffmpeg_bin_dir)
    830             add_to_PATH(x265_bin_dir)
    831             add_to_keywords(kw, 'include_dirs', x265_include_dir)
    832             add_to_keywords(kw, 'libraries', "libx265")
    833             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % x265_lib_dir)
    834             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    835             checkdirs(x265_include_dir, x265_lib_dir)
    836         elif "vpx" in pkgs_options[0]:
    837             add_to_PATH(libffmpeg_bin_dir)
    838             add_to_keywords(kw, 'include_dirs', vpx_include_dir)
    839             add_to_keywords(kw, 'libraries', *vpx_lib_names)
    840             add_to_keywords(kw, 'extra_link_args', "/NODEFAULTLIB:LIBCMT")
    841             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % vpx_lib_dir)
    842             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    843             checkdirs(vpx_include_dir, vpx_lib_dir)
    844         elif "webp" in pkgs_options[0]:
    845             add_to_PATH(webp_bin_dir)
    846             add_to_keywords(kw, 'include_dirs', webp_include_dir)
    847             add_to_keywords(kw, 'libraries', *webp_lib_names)
    848             add_to_keywords(kw, 'extra_link_args', "/LIBPATH:%s" % webp_lib_dir)
    849             add_to_keywords(kw, 'extra_link_args', "/OPT:NOREF")
    850             checkdirs(webp_include_dir, webp_lib_dir, webp_bin_dir)
    851         elif "pygobject-2.0" in pkgs_options[0]:
    852             dirs = (python_include_path,
    853                     pygtk_include_dir, atk_include_dir, gtk2_include_dir,
    854                     gtk2_base_include_dir, gdkconfig_include_dir, gdkpixbuf_include_dir,
    855                     glib_include_dir, glibconfig_include_dir,
    856                     cairo_include_dir, pango_include_dir)
    857             add_to_keywords(kw, 'include_dirs', *dirs)
    858             checkdirs(*dirs)
    859         elif "cuda" in pkgs_options[0]:
    860             add_to_keywords(kw, 'include_dirs', cuda_include_dir)
    861             checkdirs(cuda_include_dir)
    862             data_files.append(('.', glob.glob("%s/*32*.dll" % cuda_bin_dir)))
    863         else:
    864             sys.exit("ERROR: unknown package config: %s" % str(pkgs_options))
    865         if debug_ENABLED:
    866             #Od will override whatever may be specified elsewhere
    867             #and allows us to use the debug switches,
    868             #at the cost of a warning...
    869             for flag in ('/Od', '/Zi', '/DEBUG', '/RTC1', '/GS'):
    870                 add_to_keywords(kw, 'extra_compile_args', flag)
    871             add_to_keywords(kw, 'extra_link_args', "/DEBUG")
    872             kw['cython_gdb'] = True
    873         print("pkgconfig(%s,%s)=%s" % (pkgs_options, ekw, kw))
    874         return kw
    875 
    876     import py2exe    #@UnresolvedImport
    877     assert py2exe is not None
    878 
    879     #with py2exe, we don't use py_modules, we use "packages"... sigh
    880     #(and it is a little bit different too - see below)
    881     del setup_options["py_modules"]
    882     add_packages("xpra.platform.win32")
     1025    MINGW_PREFIX = os.environ.get("MINGW_PREFIX")
     1026    assert MINGW_PREFIX, "you must run this build from a MINGW environment"
     1027    if modules_ENABLED:
     1028        add_packages("xpra.platform.win32", "xpra.platform.win32.namedpipes")
    8831029    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"]
     1030
     1031    #this is where the win32 gi installer will put things:
     1032    gnome_include_path = os.environ.get("MINGW_PREFIX")
     1033
     1034    #only add the cx_freeze specific options
     1035    #only if we are packaging:
     1036    if "install_exe" in sys.argv:
     1037        #with cx_freeze, we don't use py_modules
     1038        del setup_options["py_modules"]
     1039        from cx_Freeze import setup, Executable     #@UnresolvedImport @Reimport
     1040        if not hasattr(sys, "base_prefix"):
     1041            #workaround for broken sqlite hook with python 2.7, see:
     1042            #https://github.com/anthony-tuininga/cx_Freeze/pull/272
     1043            sys.base_prefix = sys.prefix
     1044
     1045        #cx_freeze doesn't use "data_files"...
     1046        del setup_options["data_files"]
     1047        #it wants source files first, then where they are placed...
     1048        #one item at a time (no lists)
     1049        #all in its own structure called "include_files" instead of "data_files"...
     1050        def add_data_files(target_dir, files):
     1051            if verbose_ENABLED:
     1052                print("add_data_files(%s, %s)" % (target_dir, files))
     1053            assert isinstance(target_dir, str)
     1054            assert isinstance(files, (list, tuple))
     1055            for f in files:
     1056                target_file = os.path.join(target_dir, os.path.basename(f))
     1057                data_files.append((f, target_file))
     1058
     1059        #pass a potentially nested dictionary representing the tree
     1060        #of files and directories we do want to include
     1061        #relative to gnome_include_path
     1062        def add_dir(base, defs):
     1063            if verbose_ENABLED:
     1064                print("add_dir(%s, %s)" % (base, defs))
     1065            if isinstance(defs, (list, tuple)):
     1066                for sub in defs:
     1067                    if isinstance(sub, dict):
     1068                        add_dir(base, sub)
     1069                    else:
     1070                        assert isinstance(sub, str)
     1071                        filename = os.path.join(gnome_include_path, base, sub)
     1072                        if os.path.exists(filename):
     1073                            add_data_files(base, [filename])
     1074                        else:
     1075                            print("Warning: missing '%s'" % filename)
     1076            else:
     1077                assert isinstance(defs, dict)
     1078                for d, sub in defs.items():
     1079                    assert isinstance(sub, (dict, list, tuple))
     1080                    #recurse down:
     1081                    add_dir(os.path.join(base, d), sub)
     1082
     1083        def add_gi_typelib(*libs):
     1084            if verbose_ENABLED:
     1085                print("add_gi_typelib(%s)" % str(libs))
     1086            add_dir('lib',      {"girepository-1.0":    ["%s.typelib" % x for x in libs]})
     1087        def add_gi_gir(*libs):
     1088            if verbose_ENABLED:
     1089                print("add_gi_gir(%s)" % str(libs))
     1090            add_dir('share',    {"gir-1.0" :            ["%s.gir" % x for x in libs]})
     1091        #convenience method for adding GI libs and "typelib" and "gir":
     1092        def add_gi(*libs):
     1093            add_gi_typelib(*libs)
     1094            add_gi_gir(*libs)
     1095
     1096        def add_DLLs(*dll_names):
     1097            try:
     1098                do_add_DLLs("lib", *dll_names)
     1099            except Exception as e:
     1100                print("Error: failed to add DLLs: %s" % (dll_names, ))
     1101                print(" %s" % e)
     1102                sys.exit(1)
     1103
     1104        def do_add_DLLs(prefix="lib", *dll_names):
     1105            dll_names = list(dll_names)
     1106            dll_files = []
     1107            import re
     1108            version_re = re.compile(r"-[0-9\.-]+$")
     1109            dirs = os.environ.get("PATH").split(os.path.pathsep)
     1110            if os.path.exists(gnome_include_path):
     1111                dirs.insert(0, gnome_include_path)
     1112            if verbose_ENABLED:
     1113                print("add_DLLs: looking for %s in %s" % (dll_names, dirs))
     1114            for d in dirs:
     1115                if not os.path.exists(d):
     1116                    continue
     1117                for x in os.listdir(d):
     1118                    dll_path = os.path.join(d, x)
     1119                    if os.path.isdir(dll_path):
     1120                        continue
     1121                    x = x.lower()
     1122                    if prefix and not x.startswith(prefix):
     1123                        continue
     1124                    if not x.endswith(".dll"):
     1125                        continue
     1126                    #strip prefix (ie: "lib") and ".dll":
     1127                    #ie: "libatk-1.0-0.dll" -> "atk-1.0-0"
     1128                    nameversion = x[len(prefix):-4]
     1129                    if verbose_ENABLED:
     1130                        print("checking %s: %s" % (x, nameversion))
     1131                    m = version_re.search(nameversion)          #look for version part of filename
     1132                    if m:
     1133                        dll_version = m.group(0)                #found it, ie: "-1.0-0"
     1134                        dll_name = nameversion[:-len(dll_version)]  #ie: "atk"
     1135                        dll_version = dll_version.lstrip("-")   #ie: "1.0-0"
     1136                    else:
     1137                        dll_version = ""                        #no version
     1138                        dll_name = nameversion                  #ie: "libzzz.dll" -> "zzz"
     1139                    if dll_name in dll_names:
     1140                        #this DLL is on our list
     1141                        print("%s %s %s" % (dll_name.ljust(22), dll_version.ljust(10), x))
     1142                        dll_files.append(dll_path)
     1143                        dll_names.remove(dll_name)
     1144            if dll_names:
     1145                print("some DLLs could not be found:")
     1146                for x in dll_names:
     1147                    print(" - %s%s*.dll" % (prefix, x))
     1148            add_data_files("", dll_files)
     1149
     1150        #list of DLLs we want to include, without the "lib" prefix, or the version and extension
     1151        #(ie: "libatk-1.0-0.dll" -> "atk")
     1152        if sound_ENABLED or gtk3_ENABLED:
     1153            add_DLLs('gio', 'girepository', 'glib',
     1154                     'gnutls', 'gobject', 'gthread',
     1155                     'orc', 'stdc++',
     1156                     'winpthread',
     1157                     )
     1158        if gtk3_ENABLED:
     1159            add_DLLs('atk',
     1160                     #'dbus', 'dbus-glib',
     1161                     'gdk', 'gdk_pixbuf', 'gtk',
     1162                     'cairo-gobject', 'pango', 'pangocairo', 'pangoft2', 'pangowin32',
     1163                     'harfbuzz', 'harfbuzz-gobject',
     1164                     'jasper', 'epoxy',
     1165                     'intl',
     1166                     'p11-kit',
     1167                     'jpeg', 'png16', 'rsvg',
     1168                     'webp', "webpdecoder",
     1169                     'tiff',
     1170                     )
     1171
     1172        if gtk3_ENABLED:
     1173            add_dir('etc', ["fonts", "gtk-3.0", "pkcs11"])     #add "dbus-1"?
     1174            add_dir('lib', ["gdk-pixbuf-2.0", "gtk-3.0",
     1175                            "p11-kit", "pkcs11"])
     1176            add_dir('share', ["fontconfig", "fonts", "glib-2.0",        #add "dbus-1"?
     1177                              "p11-kit", "xml",
     1178                              {"locale" : ["en"]},
     1179                              {"themes" : ["Default"]}
     1180                             ])
     1181            ICONS = ["24x24", "48x48", "scalable", "cursors", "index.theme"]
     1182            for theme in ("Adwaita", ): #"hicolor"
     1183                add_dir("share/icons/"+theme, ICONS)
     1184            add_dir("share/themes/Windows-10", [
     1185                "CREDITS", "LICENSE.md", "README.md",
     1186                "gtk-3.20", "index.theme"])
     1187        if gtk3_ENABLED or sound_ENABLED:
     1188            #causes warnings:
     1189            #add_dir('lib', ["gio"])
     1190            packages.append("gi")
     1191            add_gi_typelib("Gio-2.0", "GIRepository-2.0", "Glib-2.0", "GModule-2.0",
     1192                   "GObject-2.0")
     1193        if gtk3_ENABLED:
     1194            add_gi("Atk-1.0",
     1195                   "Notify-0.7",
     1196                   "GDesktopEnums-3.0", "Soup-2.4",
     1197                   "GdkPixbuf-2.0", "Gdk-3.0", "Gtk-3.0",
     1198                   "HarfBuzz-0.0",
     1199                   "Pango-1.0", "PangoCairo-1.0", "PangoFT2-1.0",
     1200                   "Rsvg-2.0",
     1201                   )
     1202            add_gi_typelib("cairo-1.0",
     1203                           "fontconfig-2.0", "freetype2-2.0",
     1204                           "libproxy-1.0", "libxml2-2.0")
     1205            #we no longer support GtkGL:
     1206            #if opengl_ENABLED:
     1207            #    add_gi("GdkGLExt-3.0", "GtkGLExt-3.0", "GL-1.0")
     1208            add_DLLs('curl', 'soup')
     1209
     1210        if client_ENABLED:
     1211            #svg pixbuf loader:
     1212            add_DLLs("rsvg", "croco")
     1213
     1214        if client_ENABLED or server_ENABLED:
     1215            packages.append("qrencode")
     1216            add_DLLs("qrencode")
     1217
     1218        if sound_ENABLED:
     1219            add_dir("share", ["gst-plugins-base", "gstreamer-1.0"])
     1220            add_gi("Gst-1.0", "GstAllocators-1.0", "GstAudio-1.0", "GstBase-1.0",
     1221                   "GstTag-1.0")
     1222            add_DLLs('gstreamer', 'orc-test')
     1223            for p in ("app", "audio", "base", "codecparsers", "fft", "net", "video",
     1224                      "pbutils", "riff", "sdp", "rtp", "rtsp", "tag", "uridownloader",
     1225                      #I think 'coreelements' needs those (otherwise we would exclude them):
     1226                      "basecamerabinsrc", "mpegts", "photography",
     1227                      ):
     1228                add_DLLs('gst%s' % p)
     1229            #DLLs needed by the plugins:
     1230            add_DLLs("faac", "faad", "flac", "mpg123")      #"mad" is no longer included?
     1231            #add the gstreamer plugins we need:
     1232            GST_PLUGINS = ("app",
     1233                           "cutter",
     1234                           #muxers:
     1235                           "gdp", "matroska", "ogg", "isomp4",
     1236                           "audioparsers", "audiorate", "audioconvert", "audioresample", "audiotestsrc",
     1237                           "coreelements", "directsound", "directsoundsrc", "wasapi",
     1238                           #codecs:
     1239                           "opus", "opusparse", "flac", "lame", "mpg123", "speex", "faac", "faad",
     1240                           "volume", "vorbis", "wavenc", "wavpack", "wavparse",
     1241                           "autodetect",
     1242                           #no longer available: "mad"
     1243                           #untested: a52dec, voaacenc
     1244                           )
     1245            add_dir(os.path.join("lib", "gstreamer-1.0"), [("libgst%s.dll" % x) for x in GST_PLUGINS])
     1246            #END OF SOUND
     1247
     1248        if server_ENABLED:
     1249            #used by proxy server:
     1250            external_includes += ["multiprocessing", "setproctitle"]
     1251
     1252        external_includes += ["encodings"]
     1253        external_includes += ["mimetypes"]
     1254        if client_ENABLED:
     1255            #for parsing "open-command":
     1256            external_includes += ["shlex"]
     1257            #for version check:
     1258            external_includes += [
     1259                                  "ftplib", "fileinput",
     1260                                  ]
     1261            external_includes += ["urllib", "http.cookiejar", "http.client"]
     1262            #for websocket browser cookie
     1263            external_includes += ["browser_cookie3", "pyaes", "pbkdf2", "keyring"]
     1264
     1265        if dec_avcodec2_ENABLED:
     1266            #why isn't this one picked up automatically?
     1267            add_DLLs("x265")
     1268
     1269        #hopefully, cx_Freeze will fix this horror:
     1270        #(we shouldn't have to deal with DLL dependencies)
     1271        import site
     1272        lib_python = os.path.dirname(site.getsitepackages()[0])
     1273        lib_dynload_dir = os.path.join(lib_python, "lib-dynload")
     1274        add_data_files('', glob.glob("%s/zlib*dll" % lib_dynload_dir))
     1275        for x in ("io", "codecs", "abc", "_weakrefset", "encodings"):
     1276            add_data_files("lib/", glob.glob("%s/%s*" % (lib_python, x)))
     1277        #ensure that cx_freeze won't automatically grab other versions that may lay on our path:
     1278        os.environ["PATH"] = gnome_include_path+";"+os.environ.get("PATH", "")
     1279        bin_excludes = ["MSVCR90.DLL", "MFC100U.DLL"]
     1280        cx_freeze_options = {
     1281                            "includes"          : external_includes,
     1282                            "packages"          : packages,
     1283                            "include_files"     : data_files,
     1284                            "excludes"          : excludes,
     1285                            "include_msvcr"     : True,
     1286                            "bin_excludes"      : bin_excludes,
     1287                            }
     1288        #cx_Freeze v5 workarounds:
     1289        if nvenc_ENABLED or nvfbc_ENABLED:
     1290            add_packages("numpy.core._methods", "numpy.lib.format")
     1291
     1292        setup_options["options"] = {"build_exe" : cx_freeze_options}
     1293        executables = []
     1294        setup_options["executables"] = executables
     1295
     1296        def add_exe(script, icon, base_name, base="Console"):
     1297            executables.append(Executable(
     1298                        script                  = script,
     1299                        initScript              = None,
     1300                        #targetDir               = "dist",
     1301                        icon                    = "icons/%s" % icon,
     1302                        targetName              = "%s.exe" % base_name,
     1303                        base                    = base,
     1304                        ))
     1305
     1306        def add_console_exe(script, icon, base_name):
     1307            add_exe(script, icon, base_name)
     1308        def add_gui_exe(script, icon, base_name):
     1309            add_exe(script, icon, base_name, base="Win32GUI")
     1310        def add_service_exe(script, icon, base_name):
     1311            add_exe(script, icon, base_name, base="Win32Service")
     1312
     1313        #UI applications (detached from shell: no text output if ran from cmd.exe)
     1314        if (client_ENABLED or server_ENABLED) and gtk3_ENABLED:
     1315            add_gui_exe("scripts/xpra",                         "xpra.ico",         "Xpra")
     1316            add_gui_exe("win32/service/shadow_server.py",       "server-notconnected.ico",    "Xpra-Shadow")
     1317            add_gui_exe("scripts/xpra_launcher",                "xpra.ico",         "Xpra-Launcher")
     1318            add_console_exe("scripts/xpra_launcher",            "xpra.ico",         "Xpra-Launcher-Debug")
     1319            add_gui_exe("xpra/gtk_common/gtk_view_keyboard.py", "keyboard.ico",     "GTK_Keyboard_Test")
     1320            add_gui_exe("xpra/scripts/bug_report.py",           "bugs.ico",         "Bug_Report")
     1321            add_gui_exe("xpra/platform/win32/gdi_screen_capture.py", "screenshot.ico", "Screenshot")
     1322        if server_ENABLED:
     1323            add_gui_exe("scripts/auth_dialog",                  "authentication.ico", "Auth_Dialog")
     1324        if mdns_ENABLED and gtk3_ENABLED:
     1325            add_gui_exe("xpra/client/gtk_base/mdns_gui.py",     "mdns.ico",         "Xpra_Browser")
     1326        #Console: provide an Xpra_cmd.exe we can run from the cmd.exe shell
     1327        add_console_exe("scripts/xpra",                     "xpra_txt.ico",     "Xpra_cmd")
     1328        add_console_exe("xpra/scripts/version.py",          "information.ico",  "Version_info")
     1329        add_console_exe("xpra/net/net_util.py",             "network.ico",      "Network_info")
     1330        if gtk3_ENABLED:
     1331            add_console_exe("xpra/scripts/gtk_info.py",         "gtk.ico",          "GTK_info")
     1332            add_console_exe("xpra/gtk_common/keymap.py",        "keymap.ico",       "Keymap_info")
     1333            add_console_exe("xpra/platform/keyboard.py",        "keymap.ico",       "Keyboard_info")
     1334            add_gui_exe("xpra/client/gtk_base/example/tray.py", "xpra.ico",         "SystemTray_Test")
     1335            add_gui_exe("xpra/client/gtk_base/u2f_tool.py",     "authentication.ico", "U2F_Tool")
     1336        if client_ENABLED or server_ENABLED:
     1337            add_console_exe("win32/python_exec.py",             "python.ico",       "Python_exec_cmd")
     1338            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile_cmd")
     1339            add_console_exe("win32/python_execfile.py",         "python.ico",       "Python_execfile_gui")
     1340            add_console_exe("xpra/scripts/config.py",           "gears.ico",        "Config_info")
     1341        if server_ENABLED:
     1342            add_console_exe("xpra/server/auth/sqlite_auth.py",  "sqlite.ico",        "SQLite_auth_tool")
     1343            add_console_exe("xpra/server/auth/sql_auth.py",     "sql.ico",           "SQL_auth_tool")
     1344            add_console_exe("xpra/server/auth/win32_auth.py",   "authentication.ico", "System-Auth-Test")
     1345            add_console_exe("xpra/server/auth/ldap_auth.py",    "authentication.ico", "LDAP-Auth-Test")
     1346            add_console_exe("xpra/server/auth/ldap3_auth.py",   "authentication.ico", "LDAP3-Auth-Test")
     1347            add_console_exe("win32/service/proxy.py",           "xpra_txt.ico",     "Xpra-Proxy_cmd")
     1348            add_gui_exe("win32/service/proxy.py",               "xpra.ico",         "Xpra-Proxy")
     1349            add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico",     "System-Logon-Test")
     1350        if client_ENABLED:
     1351            add_console_exe("xpra/codecs/loader.py",            "encoding.ico",     "Encoding_info")
     1352            add_console_exe("xpra/platform/paths.py",           "directory.ico",    "Path_info")
     1353            add_console_exe("xpra/platform/features.py",        "features.ico",     "Feature_info")
     1354        if client_ENABLED:
     1355            add_console_exe("xpra/platform/gui.py",             "browse.ico",       "NativeGUI_info")
     1356            add_console_exe("xpra/platform/win32/gui.py",       "loop.ico",         "Events_Test")
     1357        if sound_ENABLED:
     1358            add_console_exe("xpra/sound/gstreamer_util.py",     "gstreamer.ico",    "GStreamer_info")
     1359            add_console_exe("scripts/xpra",                     "speaker.ico",      "Xpra_Audio")
     1360            add_console_exe("xpra/platform/win32/directsound.py", "speaker.ico",      "Audio_Devices")
     1361            #add_console_exe("xpra/sound/src.py",                "microphone.ico",   "Sound_Record")
     1362            #add_console_exe("xpra/sound/sink.py",               "speaker.ico",      "Sound_Play")
     1363        if opengl_ENABLED:
     1364            add_console_exe("xpra/client/gl/gl_check.py",   "opengl.ico",       "OpenGL_check")
     1365        if webcam_ENABLED:
     1366            add_console_exe("xpra/platform/webcam.py",          "webcam.ico",    "Webcam_info")
     1367            add_console_exe("xpra/scripts/show_webcam.py",          "webcam.ico",    "Webcam_Test")
     1368        if printing_ENABLED:
     1369            add_console_exe("xpra/platform/printing.py",        "printer.ico",     "Print")
     1370            add_console_exe("xpra/platform/win32/pdfium.py",    "printer.ico",     "PDFIUM_Print")
     1371            do_add_DLLs("", "pdfium")
     1372        if nvenc_ENABLED:
     1373            add_console_exe("xpra/codecs/nv_util.py",                   "nvidia.ico",   "NVidia_info")
     1374        if nvfbc_ENABLED:
     1375            add_console_exe("xpra/codecs/nvfbc/capture.py",             "nvidia.ico",   "NvFBC_capture")
     1376        if nvfbc_ENABLED or nvenc_ENABLED:
     1377            add_console_exe("xpra/codecs/cuda_common/cuda_context.py",  "cuda.ico",     "CUDA_info")
     1378
     1379        if example_ENABLED:
     1380            add_gui_exe("xpra/client/gtk_base/example/colors.py",               "encoding.ico",     "Colors")
     1381            add_gui_exe("xpra/client/gtk_base/example/colors_gradient.py",      "encoding.ico",     "Colors-Gradient")
     1382            add_gui_exe("xpra/client/gtk_base/example/colors_plain.py",         "encoding.ico",     "Colors-Plain")
     1383            add_gui_exe("xpra/client/gtk_base/example/bell.py",                 "bell.ico",         "Bell")
     1384            add_gui_exe("xpra/client/gtk_base/example/transparent_colors.py",   "transparent.ico",  "Transparent-Colors")
     1385            add_gui_exe("xpra/client/gtk_base/example/transparent_window.py",   "transparent.ico",  "Transparent-Window")
     1386            add_gui_exe("xpra/client/gtk_base/example/fontrendering.py",        "font.ico",         "Font-Rendering")
     1387
     1388    if ("install_exe" in sys.argv) or ("install" in sys.argv):
     1389        #FIXME: how do we figure out what target directory to use?
     1390        print("calling build_xpra_conf in-place")
     1391        #building etc files in-place:
     1392        if data_ENABLED:
     1393            build_xpra_conf(".")
     1394            add_data_files('etc/xpra', glob.glob("etc/xpra/*conf"))
     1395            add_data_files('etc/xpra', glob.glob("etc/xpra/nvenc*.keys"))
     1396            add_data_files('etc/xpra', glob.glob("etc/xpra/nvfbc*.keys"))
     1397            add_data_files('etc/xpra/conf.d', glob.glob("etc/xpra/conf.d/*conf"))
     1398        #build minified html5 client in temporary build dir:
     1399        if "clean" not in sys.argv and html5_ENABLED:
     1400            install_html5(os.path.join(install, "www"), )
     1401            for k,v in glob_recurse("build/www").items():
     1402                if k!="":
     1403                    k = os.sep+k
     1404                add_data_files('www'+k, v)
     1405
     1406    if data_ENABLED:
     1407        add_data_files("",              ["win32/website.url"])
     1408        add_data_files("",              ["win32\\DirectShow.tlb"])
     1409
    9071410    remove_packages(*external_excludes)
     1411    external_includes.append("pyu2f")
     1412    external_includes.append("mmap")
     1413    external_includes.append("comtypes")    #used by webcam and netdev_query
     1414    remove_packages("comtypes.gen")         #this is generated at runtime
     1415                                            #but we still have to remove the empty directory by hand
     1416                                            #afterwards because cx_freeze does weird things (..)
    9081417    remove_packages(#not used on win32:
    909                     "mmap",
    9101418                    #we handle GL separately below:
    9111419                    "OpenGL", "OpenGL_accelerate",
     
    9131421                    "ctypes.macholib")
    9141422
    915     if not cyxor_ENABLED or opengl_ENABLED:
    916         #we need numpy for opengl or as a fallback for the Cython xor module
    917         py2exe_includes.append("numpy")
     1423    if webcam_ENABLED and False:
     1424        external_includes.append("cv2")
    9181425    else:
    919         remove_packages("numpy",
    920                         "unittest", "difflib",  #avoid numpy warning (not an error)
     1426        remove_packages("cv2")
     1427
     1428    if nvenc_ENABLED or nvfbc_ENABLED:
     1429        external_includes.append("numpy")
     1430    else:
     1431        remove_packages("unittest", "difflib",  #avoid numpy warning (not an error)
    9211432                        "pydoc")
    9221433
    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:
     1434    #make sure we don't include the gstreamer 0.10 "pygst" bindings:
     1435    remove_packages("pygst", "gst", "gst.extend")
     1436
     1437    #add subset of PyOpenGL modules (only when installing):
     1438    if opengl_ENABLED and "install_exe" in sys.argv:
    9291439        #for this hack to work, you must add "." to the sys.path
    9301440        #so python can load OpenGL from the install directory
    9311441        #(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():
     1442        #but we re-add those two directories to the library.zip as part of the build script
     1443        import OpenGL
     1444        print("*** copying PyOpenGL modules to %s ***" % install)
     1445        glmodules = {
     1446            "OpenGL" : OpenGL,
     1447            }
     1448        try:
     1449            import OpenGL_accelerate        #@UnresolvedImport
     1450        except ImportError as e:
     1451            print("Warning: missing OpenGL_accelerate module")
     1452            print(" %s" % e)
     1453        else:
     1454            glmodules["OpenGL_accelerate"] = OpenGL_accelerate
     1455        for module_name, module in glmodules.items():
    9361456            module_dir = os.path.dirname(module.__file__ )
    9371457            try:
    9381458                shutil.copytree(
    939                     module_dir, os.path.join("dist", module_name),
    940                     ignore = shutil.ignore_patterns("Tk")
     1459                    module_dir, os.path.join(install, "lib", module_name),
     1460                    ignore = shutil.ignore_patterns(
     1461                        "Tk", "AGL", "EGL",
     1462                        "GLX", "GLX.*", "_GLX.*",
     1463                        "GLE", "GLES1", "GLES2", "GLES3",
     1464                        )
    9411465                )
    942             except WindowsError, error:     #@UndefinedVariable
    943                 if not "already exists" in str( error ):
     1466                print("copied %s to %s/%s" % (module_dir, install, module_name))
     1467            except Exception as e:
     1468                if not isinstance(e, WindowsError) or ("already exists" not in str(e)): #@UndefinedVariable
    9441469                    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 
     1470
     1471        add_data_files('', glob.glob("win32\\bundle-extra\\*"))
     1472
     1473    #END OF win32
    9831474#*******************************************************************************
    9841475else:
    9851476    #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"]))
     1477    if is_Fedora() or is_CentOS() or is_RedHat() or FREEBSD:
     1478        libexec = "libexec"
     1479    else:
     1480        libexec = "lib"
     1481    if LINUX:
     1482        if scripts_ENABLED:
     1483            scripts += ["scripts/xpra_udev_product_version", "scripts/xpra_signal_listener"]
     1484        libexec_scripts = []
     1485        if xdg_open_ENABLED:
     1486            libexec_scripts += ["scripts/xdg-open", "scripts/gnome-open", "scripts/gvfs-open"]
     1487        if server_ENABLED:
     1488            libexec_scripts.append("scripts/auth_dialog")
     1489        if libexec_scripts:
     1490            add_data_files("%s/xpra/" % libexec, libexec_scripts)
     1491    if data_ENABLED:
     1492        man_path = "share/man"
     1493        if OPENBSD or FREEBSD:
     1494            man_path = "man"
     1495        man_pages = ["man/xpra.1", "man/xpra_launcher.1"]
     1496        if not OSX:
     1497            man_pages.append("man/run_scaled.1")
     1498        add_data_files("%s/man1" % man_path,  man_pages)
     1499        add_data_files("share/applications",  glob.glob("xdg/*.desktop"))
     1500        add_data_files("share/mime/packages", ["xdg/application-x-xpraconfig.xml"])
     1501        add_data_files("share/icons",         ["xdg/xpra.png", "xdg/xpra-mdns.png", "xdg/xpra-shadow.png"])
     1502        add_data_files("share/metainfo",      ["xdg/xpra.appdata.xml"])
     1503
     1504    #here, we override build and install so we can
     1505    #generate our /etc/xpra/xpra.conf
     1506    class build_override(build):
     1507        def run(self):
     1508            build.run(self)
     1509            self.run_command("build_conf")
     1510
     1511    class build_conf(build):
     1512        def run(self):
     1513            try:
     1514                build_base = self.distribution.command_obj['build'].build_base
     1515            except:
     1516                build_base = self.build_base
     1517            build_xpra_conf(build_base)
     1518
     1519    class install_data_override(install_data):
     1520        def run(self):
     1521            print("install_data_override: install_dir=%s" % self.install_dir)
     1522            if html5_ENABLED:
     1523                install_html5(os.path.join(self.install_dir, "%s/www" % share_xpra))
     1524            install_data.run(self)
     1525
     1526            root_prefix = self.install_dir.rstrip("/")
     1527            if root_prefix.endswith("/usr"):
     1528                root_prefix = root_prefix[:-4]    #ie: "/" or "/usr/src/rpmbuild/BUILDROOT/xpra-0.18.0-0.20160513r12573.fc23.x86_64/"
     1529            build_xpra_conf(root_prefix)
     1530
     1531            def copytodir(src, dst_dir, dst_name=None, chmod=0o644, subs=None):
     1532                #convert absolute paths:
     1533                if dst_dir.startswith("/"):
     1534                    dst_dir = root_prefix+dst_dir
     1535                else:
     1536                    dst_dir = self.install_dir.rstrip("/")+"/"+dst_dir
     1537                #make sure the target directory exists:
     1538                self.mkpath(dst_dir)
     1539                #generate the target filename:
     1540                filename = os.path.basename(src)
     1541                dst_file = os.path.join(dst_dir, dst_name or filename)
     1542                #copy it
     1543                print("copying %s -> %s (%s)" % (src, dst_dir, oct(chmod)))
     1544                data = load_binary_file(src)
     1545                if subs:
     1546                    for k,v in subs.items():
     1547                        data = data.replace(k, v)
     1548                with open(dst_file, "wb") as f:
     1549                    f.write(data)
     1550                if chmod:
     1551                    print("chmod(%s, %s)" % (dst_file, oct(chmod)))
     1552                    os.chmod(dst_file, chmod)
     1553
     1554            if printing_ENABLED and POSIX:
     1555                #install "/usr/lib/cups/backend" with 0700 permissions:
     1556                lib_cups = "lib/cups"
     1557                if FREEBSD:
     1558                    lib_cups = "libexec/cups"
     1559                copytodir("cups/xpraforwarder", "%s/backend" % lib_cups, chmod=0o700)
     1560
     1561            if x11_ENABLED:
     1562                #install xpra_Xdummy if we need it:
     1563                xvfb_command = detect_xorg_setup()
     1564                if any(x.find("xpra_Xdummy")>=0 for x in (xvfb_command or [])) or Xdummy_wrapper_ENABLED is True:
     1565                    copytodir("scripts/xpra_Xdummy", "bin", chmod=0o755)
     1566                #install xorg*.conf, cuda.conf and nvenc.keys:
     1567                etc_xpra_files = ["xorg.conf"]
     1568                if uinput_ENABLED:
     1569                    etc_xpra_files.append("xorg-uinput.conf")
     1570                if nvenc_ENABLED or nvfbc_ENABLED:
     1571                    etc_xpra_files.append("cuda.conf")
     1572                if nvenc_ENABLED:
     1573                    etc_xpra_files.append("nvenc.keys")
     1574                if nvfbc_ENABLED:
     1575                    etc_xpra_files.append("nvfbc.keys")
     1576                for x in etc_xpra_files:
     1577                    copytodir("etc/xpra/%s" % x, "/etc/xpra")
     1578                copytodir("etc/X11/xorg.conf.d/90-xpra-virtual.conf", "/etc/X11/xorg.conf.d/")
     1579
     1580            if pam_ENABLED:
     1581                copytodir("etc/pam.d/xpra", "/etc/pam.d")
     1582
     1583            systemd_dir = "/lib/systemd/system"
     1584            if service_ENABLED:
     1585                #Linux init service:
     1586                subs = {}
     1587                if os.path.exists("/etc/sysconfig"):
     1588                    copytodir("etc/sysconfig/xpra", "/etc/sysconfig")
     1589                elif os.path.exists("/etc/default"):
     1590                    copytodir("etc/sysconfig/xpra", "/etc/default")
     1591                    subs[b"/etc/sysconfig"] = b"/etc/default"
     1592                if os.path.exists("/bin/systemctl"):
     1593                    if sd_listen_ENABLED:
     1594                        copytodir("service/xpra.service", systemd_dir,
     1595                                  subs=subs)
     1596                    else:
     1597                        copytodir("service/xpra-nosocketactivation.service", systemd_dir,
     1598                                  dst_name="xpra.service", subs=subs)
     1599                else:
     1600                    copytodir("service/xpra", "/etc/init.d")
     1601            if sd_listen_ENABLED:
     1602                copytodir("service/xpra.socket", systemd_dir)
     1603            if dbus_ENABLED and proxy_ENABLED:
     1604                copytodir("dbus/xpra.conf", "/etc/dbus-1/system.d")
     1605
     1606
     1607    # add build_conf to build step
     1608    cmdclass.update({
     1609             'build'        : build_override,
     1610             'build_conf'   : build_conf,
     1611             'install_data' : install_data_override,
     1612             })
    9971613
    9981614    if OSX:
     1615        #pyobjc needs email.parser
     1616        external_includes += ["email", "uu", "urllib", "objc", "cups", "six"]
     1617        external_includes += ["kerberos", "future", "pyu2f", "paramiko", "nacl"]
    9991618        #OSX package names (ie: gdk-x11-2.0 -> gdk-2.0, etc)
    10001619        PYGTK_PACKAGES += ["gdk-2.0", "gtk+-2.0"]
    10011620        add_packages("xpra.platform.darwin")
     1621        remove_packages("xpra.platform.win32", "xpra.platform.xposix")
     1622        #to support GStreamer 1.x we need this:
     1623        modules.append("importlib")
     1624        modules.append("mimetypes")
     1625        external_excludes.append("numpy")
    10021626    else:
    10031627        PYGTK_PACKAGES += ["gdk-x11-2.0", "gtk+-x11-2.0"]
    10041628        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")
     1629        remove_packages("xpra.platform.win32", "xpra.platform.darwin")
     1630        if data_ENABLED:
     1631            #not supported by all distros, but doesn't hurt to install them anyway:
     1632            for x in ("tmpfiles.d", "sysusers.d"):
     1633                add_data_files("lib/%s" % x, ["%s/xpra.conf" % x])
     1634            if uinput_ENABLED:
     1635                add_data_files("lib/udev/rules.d/", ["udev/rules.d/71-xpra-virtual-pointer.rules"])
    10081636
    10091637    #gentoo does weird things, calls --no-compile with build *and* install
    10101638    #then expects to find the cython modules!? ie:
    1011     #> python2.7 setup.py build -b build-2.7 install --no-compile --root=/var/tmp/portage/x11-wm/xpra-0.7.0/temp/images/2.7
     1639    #> python2.7 setup.py build -b build-2.7 install --no-compile \
     1640    # --root=/var/tmp/portage/x11-wm/xpra-0.7.0/temp/images/2.7
    10121641    #otherwise we use the flags to skip pkgconfig
    10131642    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))
     1643        pkgconfig = no_pkgconfig
    10331644
    10341645    if OSX and "py2app" in sys.argv:
     
    10391650        del setup_options["py_modules"]
    10401651        scripts = []
    1041         def cython_add(*args, **kwargs):
     1652        def cython_add(*_args, **_kwargs):
    10421653            pass
    10431654
     
    10451656        remove_packages(*external_excludes)
    10461657
    1047         Plist = {"CFBundleDocumentTypes" : {
    1048                         "CFBundleTypeExtensions"    : ["Xpra"],
    1049                         "CFBundleTypeName"          : "Xpra Session Config File",
    1050                         "CFBundleName"              : "Xpra",
    1051                         "CFBundleTypeRole"          : "Viewer",
    1052                         }}
     1658        try:
     1659            from xpra.src_info import REVISION
     1660        except ImportError:
     1661            REVISION = "unknown"
     1662        Plist = {
     1663            "CFBundleDocumentTypes" : {
     1664                "CFBundleTypeExtensions"    : ["Xpra"],
     1665                "CFBundleTypeName"          : "Xpra Session Config File",
     1666                "CFBundleName"              : "Xpra",
     1667                "CFBundleTypeRole"          : "Viewer",
     1668                },
     1669            "CFBundleGetInfoString" : "%s-r%s (c) 2012-2020 https://xpra.org/" % (XPRA_VERSION, REVISION),
     1670            "CFBundleIdentifier"            : "org.xpra.xpra",
     1671            }
    10531672        #Note: despite our best efforts, py2app will not copy all the modules we need
    10541673        #so the make-app.sh script still has to hack around this problem.
    10551674        add_modules(*external_includes)
     1675        #needed by python-lz4:
     1676        add_modules("distutils")
    10561677        py2app_options = {
    10571678            'iconfile'          : '../osx/xpra.icns',
     
    10651686            }
    10661687        setup_options["options"] = {"py2app": py2app_options}
    1067         setup_options["app"]     = ["xpra/client/gtk2/client_launcher.py"]
     1688        setup_options["app"]     = ["xpra/scripts/main.py"]
     1689
     1690    if OSX:
     1691        #simply adding the X11 path to PKG_CONFIG_PATH breaks things in mysterious ways,
     1692        #so instead we have to query each package seperately and merge the results:
     1693        def osx_pkgconfig(*pkgs_options, **ekw):
     1694            kw = dict(ekw)
     1695            for pkg in pkgs_options:
     1696                saved_pcp = os.environ.get("PKG_CONFIG_PATH")
     1697                if pkg.lower().startswith("x"):
     1698                    os.environ["PKG_CONFIG_PATH"] = "/usr/X11/lib/pkgconfig"
     1699                #print("exec_pkgconfig(%s, %s)", pkg, kw)
     1700                kw = exec_pkgconfig(pkg, **kw)
     1701                os.environ["PKG_CONFIG_PATH"] = saved_pcp
     1702            return kw
     1703
     1704        pkgconfig = osx_pkgconfig
     1705
     1706
     1707if scripts_ENABLED:
     1708    scripts += ["scripts/xpra", "scripts/xpra_launcher"]
     1709    if not OSX and not WIN32:
     1710        scripts.append("scripts/run_scaled")
     1711toggle_modules(WIN32, "xpra/scripts/win32_service")
     1712
     1713if data_ENABLED:
     1714    add_data_files(share_xpra,                      ["README", "COPYING"])
     1715    add_data_files(share_xpra,                      ["bell.wav"])
     1716    add_data_files("%s/http-headers" % share_xpra,   glob.glob("http-headers/*"))
     1717    ICONS = glob.glob("icons/*")
     1718    if WIN32:
     1719        ICONS += glob.glob("icons/*ico")
     1720    add_data_files("%s/icons" % share_xpra,          ICONS)
     1721
     1722    add_data_files("%s/content-type" % share_xpra,   glob.glob("content-type/*"))
     1723    add_data_files("%s/content-categories" % share_xpra, glob.glob("content-categories/*"))
     1724    add_data_files("%s/css" % share_xpra,            glob.glob("css/*"))
    10681725
    10691726
    10701727if 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 
     1728    if WIN32 or OSX:
     1729        external_includes.append("ssl")
     1730        external_includes.append("_ssl")
     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
     1744if modules_ENABLED:
     1745    add_packages("xpra.buffers")
     1746    buffers_pkgconfig = pkgconfig(optimize=3)
     1747    import platform
     1748    if platform.machine()=="i386":
     1749        #this may well be sub-optimal:
     1750        add_to_keywords(buffers_pkgconfig, "extra_compile_args", "-mfpmath=387")
     1751    if cython_ENABLED:
     1752        cython_add(Extension("xpra.buffers.membuf",
     1753                    ["xpra/buffers/membuf.pyx"]+membuffers_c, **buffers_pkgconfig))
     1754
     1755
     1756toggle_packages(dbus_ENABLED, "xpra.dbus")
     1757toggle_packages(mdns_ENABLED, "xpra.net.mdns")
     1758toggle_packages(websockets_ENABLED, "xpra.net.websockets", "xpra.net.websockets.headers")
     1759toggle_packages(server_ENABLED or proxy_ENABLED, "xpra.server", "xpra.server.auth")
     1760toggle_packages(rfb_ENABLED, "xpra.server.rfb")
     1761toggle_packages(proxy_ENABLED, "xpra.server.proxy")
     1762toggle_packages(server_ENABLED, "xpra.server.window")
     1763toggle_packages(server_ENABLED or shadow_ENABLED, "xpra.server.mixins", "xpra.server.source")
     1764toggle_packages(shadow_ENABLED, "xpra.server.shadow")
     1765toggle_packages(server_ENABLED or client_ENABLED, "xpra.clipboard")
     1766toggle_packages(x11_ENABLED and dbus_ENABLED and server_ENABLED, "xpra.x11.dbus")
     1767toggle_packages(notifications_ENABLED, "xpra.notifications")
     1768
     1769#cannot use toggle here as cx_Freeze will complain if we try to exclude this module:
     1770if dbus_ENABLED and server_ENABLED:
     1771    add_packages("xpra.server.dbus")
     1772
     1773if OSX:
     1774    quartz_pkgconfig = pkgconfig("gtk+-3.0", "pygobject-3.0")
     1775    add_to_keywords(quartz_pkgconfig, 'extra_compile_args',
     1776                "-ObjC",
     1777                "-framework", "AppKit",
     1778                "-I/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/",
     1779                "-I/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/")
     1780    cython_add(Extension("xpra.platform.darwin.gdk3_bindings",
     1781            ["xpra/platform/darwin/gdk3_bindings.pyx", "xpra/platform/darwin/transparency_glue.m"],
     1782            language="objc",
     1783            **quartz_pkgconfig
     1784            ))
     1785
     1786if cython_ENABLED:
     1787    monotonic_time_pkgconfig = pkgconfig()
     1788    if not OSX and not WIN32 and not OPENBSD:
     1789        add_to_keywords(monotonic_time_pkgconfig, 'extra_link_args', "-lrt")
     1790    cython_add(Extension("xpra.monotonic_time",
     1791                ["xpra/monotonic_time.pyx", "xpra/monotonic_ctime.c"],
     1792                **monotonic_time_pkgconfig
     1793                ))
     1794
     1795
     1796toggle_packages(x11_ENABLED, "xpra.x11", "xpra.x11.bindings")
    10841797if x11_ENABLED:
    1085     make_constants("xpra", "x11", "bindings", "constants")
    1086     make_constants("xpra", "x11", "gtk_x11", "constants")
    1087 
    10881798    cython_add(Extension("xpra.x11.bindings.wait_for_x_server",
    10891799                ["xpra/x11/bindings/wait_for_x_server.pyx"],
     
    10981808                **pkgconfig("x11")
    10991809                ))
     1810    cython_add(Extension("xpra.x11.bindings.posix_display_source",
     1811                ["xpra/x11/bindings/posix_display_source.pyx"],
     1812                **pkgconfig("x11")
     1813                ))
     1814
    11001815    cython_add(Extension("xpra.x11.bindings.randr_bindings",
    11011816                ["xpra/x11/bindings/randr_bindings.pyx"],
     
    11041819    cython_add(Extension("xpra.x11.bindings.keyboard_bindings",
    11051820                ["xpra/x11/bindings/keyboard_bindings.pyx"],
    1106                 **pkgconfig("x11", "xtst", "xfixes")
     1821                **pkgconfig("x11", "xtst", "xfixes", "xkbfile")
    11071822                ))
    11081823
    11091824    cython_add(Extension("xpra.x11.bindings.window_bindings",
    11101825                ["xpra/x11/bindings/window_bindings.pyx"],
    1111                 **pkgconfig("xtst", "xfixes", "xcomposite", "xdamage")
     1826                **pkgconfig("x11", "xtst", "xfixes", "xcomposite", "xdamage", "xext")
    11121827                ))
    11131828    cython_add(Extension("xpra.x11.bindings.ximage",
    11141829                ["xpra/x11/bindings/ximage.pyx"],
    1115                 **pkgconfig("xcomposite", "xdamage", "xext")
     1830                **pkgconfig("x11", "xext", "xcomposite")
    11161831                ))
    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)
     1832if xinput_ENABLED:
     1833    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1834                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1835                **pkgconfig("x11", "xi")
    11221836                ))
    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)
     1837
     1838toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
     1839toggle_packages(server_ENABLED and gtk_x11_ENABLED, "xpra.x11.models")
     1840if gtk_x11_ENABLED:
     1841    add_packages("xpra.x11.gtk3")
     1842    #GTK3 display source:
     1843    cython_add(Extension("xpra.x11.gtk3.gdk_display_source",
     1844                ["xpra/x11/gtk3/gdk_display_source.pyx"],
     1845                **pkgconfig("gdk-3.0")
    11271846                ))
    1128 
    1129 
    1130 toggle_packages(argb_ENABLED, "xpra.codecs.argb")
    1131 if argb_ENABLED:
     1847    cython_add(Extension("xpra.x11.gtk3.gdk_bindings",
     1848                ["xpra/x11/gtk3/gdk_bindings.pyx", "xpra/x11/gtk3/gdk_x11_macros.c"],
     1849                **pkgconfig("gdk-3.0")
     1850                ))
     1851
     1852if client_ENABLED and gtk3_ENABLED:
     1853    #cairo workaround:
     1854    cython_add(Extension("xpra.client.gtk3.cairo_workaround",
     1855                ["xpra/client/gtk3/cairo_workaround.pyx"],
     1856                **pkgconfig("py3cairo")
     1857                ))
     1858
     1859if client_ENABLED or server_ENABLED:
     1860    add_packages("xpra.codecs.argb")
     1861    argb_pkgconfig = pkgconfig(optimize=3)
    11321862    cython_add(Extension("xpra.codecs.argb.argb",
    1133                 ["xpra/codecs/argb/argb.pyx"]))
     1863                ["xpra/codecs/argb/argb.pyx"], **argb_pkgconfig))
     1864
     1865
     1866#build tests, but don't install them:
     1867toggle_packages(tests_ENABLED, "unit")
    11341868
    11351869
    11361870if bundle_tests_ENABLED:
    11371871    #bundle the tests directly (not in library.zip):
    1138     for k,v in glob_recurse("tests").items():
    1139         if (k!=""):
     1872    for k,v in glob_recurse("unit").items():
     1873        if k!="":
    11401874            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:
     1875        add_data_files("unit"+k, v)
     1876
     1877#python-cryptography needs workarounds for bundling:
     1878if crypto_ENABLED and (OSX or WIN32):
     1879    external_includes.append("_ssl")
     1880    external_includes.append("cffi")
     1881    external_includes.append("_cffi_backend")
     1882    external_includes.append("cryptography")
     1883    external_includes.append("idna")
     1884    external_includes.append("idna.idnadata")
     1885    external_includes.append("pkg_resources._vendor.packaging")
     1886    external_includes.append("pkg_resources._vendor.packaging.requirements")
     1887    external_includes.append("pkg_resources._vendor.pyparsing")
     1888    add_modules("cryptography.hazmat.bindings._openssl")
     1889    add_modules("cryptography.hazmat.bindings._constant_time")
     1890    add_modules("cryptography.hazmat.bindings._padding")
     1891    add_modules("cryptography.hazmat.backends.openssl")
     1892    add_modules("cryptography.fernet")
     1893    if WIN32:
     1894        external_includes.append("appdirs")
     1895
     1896#special case for client: cannot use toggle_packages which would include gtk3, etc:
    11441897if 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")
    1147 toggle_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")
    1150 toggle_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")
     1898    add_modules("xpra.client")
     1899    add_packages("xpra.client.mixins", "xpra.client.auth")
     1900    add_modules("xpra.scripts.gtk_info")
     1901    add_modules("xpra.scripts.show_webcam")
     1902if gtk3_ENABLED:
     1903    add_modules("xpra.scripts.bug_report")
     1904toggle_packages((client_ENABLED and gtk3_ENABLED) or sound_ENABLED or server_ENABLED, "xpra.gtk_common")
     1905toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk3")
     1906toggle_packages((client_ENABLED and gtk3_ENABLED) or (sound_ENABLED and WIN32 and MINGW_PREFIX), "gi")
     1907toggle_packages(client_ENABLED and gtk3_ENABLED, "xpra.client.gtk_base")
     1908toggle_packages(client_ENABLED and opengl_ENABLED and gtk3_ENABLED, "xpra.client.gl.gtk3")
     1909toggle_packages(client_ENABLED and gtk3_ENABLED and example_ENABLED, "xpra.client.gtk_base.example")
     1910if client_ENABLED and WIN32 and MINGW_PREFIX:
     1911    propsys_pkgconfig = pkgconfig()
     1912    if debug_ENABLED:
     1913        add_to_keywords(propsys_pkgconfig, 'extra_compile_args', "-DDEBUG")
     1914    add_to_keywords(propsys_pkgconfig, 'extra_link_args', "-luuid", "-lshlwapi", "-lole32", "-static-libgcc")
     1915    cython_add(Extension("xpra.platform.win32.propsys",
     1916                ["xpra/platform/win32/propsys.pyx", "xpra/platform/win32/setappid.cpp"],
     1917                language="c++",
     1918                **propsys_pkgconfig))
     1919
     1920if client_ENABLED or server_ENABLED:
     1921    add_modules("xpra.codecs")
     1922toggle_packages(keyboard_ENABLED, "xpra.keyboard")
     1923if client_ENABLED or server_ENABLED:
     1924    add_modules(
     1925        "xpra.scripts.config",
     1926        "xpra.scripts.parsing",
     1927        "xpra.scripts.exec_util",
     1928        "xpra.scripts.fdproxy",
     1929        "xpra.scripts.version",
     1930        )
     1931if server_ENABLED or proxy_ENABLED:
     1932    add_modules("xpra.scripts.server")
     1933if WIN32 and client_ENABLED and gtk3_ENABLED:
     1934    add_modules("xpra.scripts.gtk_info")
     1935
     1936toggle_packages(not WIN32, "xpra.platform.pycups_printing")
     1937toggle_packages(client_ENABLED and opengl_ENABLED, "xpra.client.gl")
     1938
     1939toggle_modules(sound_ENABLED, "xpra.sound")
     1940toggle_modules(sound_ENABLED and not (OSX or WIN32), "xpra.sound.pulseaudio")
    11541941
    11551942toggle_packages(clipboard_ENABLED, "xpra.clipboard")
    11561943if clipboard_ENABLED:
    1157     cython_add(Extension("xpra.gtk_common.gdk_atoms",
    1158                 ["xpra/gtk_common/gdk_atoms.pyx"],
    1159                 **pkgconfig(*PYGTK_PACKAGES)
     1944    cython_add(Extension("xpra.gtk_common.gtk3.gdk_atoms",
     1945                         ["xpra/gtk_common/gtk3/gdk_atoms.pyx"],
     1946                         **pkgconfig("gtk+-3.0")
     1947                         ))
     1948toggle_packages(clipboard_ENABLED or gtk3_ENABLED, "xpra.gtk_common.gtk3")
     1949if gtk3_ENABLED:
     1950    cython_add(Extension("xpra.gtk_common.gtk3.gdk_bindings",
     1951                ["xpra/gtk_common/gtk3/gdk_bindings.pyx"],
     1952                **pkgconfig("gtk+-3.0", "pygobject-3.0")
    11601953                ))
    11611954
    1162 if cyxor_ENABLED:
     1955O3_pkgconfig = pkgconfig(optimize=3)
     1956toggle_packages(client_ENABLED or server_ENABLED, "xpra.codecs.xor")
     1957if client_ENABLED or server_ENABLED:
    11631958    cython_add(Extension("xpra.codecs.xor.cyxor",
    11641959                ["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")
     1960                **O3_pkgconfig))
     1961if client_ENABLED or server_ENABLED or shadow_ENABLED:
     1962    cython_add(Extension("xpra.rectangle",
     1963                ["xpra/rectangle.pyx"],
     1964                **O3_pkgconfig))
     1965
     1966if server_ENABLED or shadow_ENABLED:
     1967    cython_add(Extension("xpra.server.cystats",
     1968                ["xpra/server/cystats.pyx"],
     1969                **O3_pkgconfig))
     1970    cython_add(Extension("xpra.server.window.motion",
     1971                ["xpra/server/window/motion.pyx"],
     1972                **O3_pkgconfig))
     1973
     1974if sd_listen_ENABLED:
     1975    sdp = pkgconfig("libsystemd")
     1976    cython_add(Extension("xpra.platform.xposix.sd_listen",
     1977                ["xpra/platform/xposix/sd_listen.pyx"],
     1978                **sdp))
     1979
     1980
    11781981toggle_packages(enc_proxy_ENABLED, "xpra.codecs.enc_proxy")
    11791982
     1983toggle_packages(nvfbc_ENABLED, "xpra.codecs.nvfbc")
     1984if nvfbc_ENABLED:
     1985    nvfbc_pkgconfig = pkgconfig("nvfbc")
     1986    if WIN32:
     1987        add_to_keywords(nvfbc_pkgconfig, 'extra_compile_args', "-Wno-endif-labels")
     1988    platform = sys.platform.rstrip("0123456789")
     1989    cython_add(Extension("xpra.codecs.nvfbc.fbc_capture_%s" % platform,
     1990                         ["xpra/codecs/nvfbc/fbc_capture_%s.pyx" % platform],
     1991                         language="c++",
     1992                         **nvfbc_pkgconfig))
     1993
    11801994toggle_packages(nvenc_ENABLED, "xpra.codecs.nvenc")
     1995toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.cuda_common")
     1996toggle_packages(nvenc_ENABLED or nvfbc_ENABLED, "xpra.codecs.nv_util")
     1997
     1998if nvenc_ENABLED and cuda_kernels_ENABLED:
     1999    #find nvcc:
     2000    path_options = os.environ.get("PATH", "").split(os.path.pathsep)
     2001    CUDA_VERSIONS = ["11.2", "11.1", "11.0", "10.2", "10.1", "10.0", "9.2", "9.1", "9.0", "8.0", "7.5", ]
     2002    if WIN32:
     2003        external_includes += ["pycuda"]
     2004        nvcc_exe = "nvcc.exe"
     2005        CUDA_DIR = os.environ.get("CUDA_DIR", "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA")
     2006        path_options = [os.path.join(CUDA_DIR, "v%s" % x, "bin") for x in CUDA_VERSIONS] + path_options
     2007        #pycuda may link against curand, find it and ship it:
     2008        for p in path_options:
     2009            if os.path.exists(p):
     2010                add_data_files("", glob.glob("%s\\curand64*.dll" % p))
     2011                add_data_files("", glob.glob("%s\\cudart64*.dll" % p))
     2012                break
     2013    else:
     2014        nvcc_exe = "nvcc"
     2015        for v in [""]+CUDA_VERSIONS:
     2016            suffix = ""
     2017            if v:
     2018                suffix = "-%s" % v
     2019            path_options += ["/usr/local/cuda%s/bin" % suffix, "/opt/cuda%s/bin" % suffix]
     2020    options = [os.path.join(x, nvcc_exe) for x in path_options]
     2021    def which(cmd):
     2022        try:
     2023            code, out, _ = get_status_output(["which", cmd])
     2024            if code==0:
     2025                return out
     2026        except:
     2027            pass
     2028        return None
     2029    #prefer the one we find on the $PATH, if any:
     2030    try:
     2031        v = which(nvcc_exe)
     2032        if v and (v not in options):
     2033            options.insert(0, v)
     2034    except:
     2035        pass
     2036    nvcc_versions = {}
     2037    def get_nvcc_version(command):
     2038        if not os.path.exists(command):
     2039            return None
     2040        code, out, _ = get_status_output([command, "--version"])
     2041        if code!=0:
     2042            return None
     2043        vpos = out.rfind(", V")
     2044        if vpos>0:
     2045            version = out[vpos+3:].split("\n")[0]
     2046            version_str = " version %s" % version
     2047        else:
     2048            version = "0"
     2049            version_str = " unknown version!"
     2050        print("found CUDA compiler: %s%s" % (filename, version_str))
     2051        return tuple(int(x) for x in version.split("."))
     2052    for filename in options:
     2053        vnum = get_nvcc_version(filename)
     2054        if vnum:
     2055            nvcc_versions[vnum] = filename
     2056    assert nvcc_versions, "cannot find nvcc compiler!"
     2057    #choose the most recent one:
     2058    version, nvcc = list(reversed(sorted(nvcc_versions.items())))[0]
     2059    if len(nvcc_versions)>1:
     2060        print(" using version %s from %s" % (version, nvcc))
     2061    if WIN32:
     2062        cuda_path = os.path.dirname(nvcc)           #strip nvcc.exe
     2063        cuda_path = os.path.dirname(cuda_path)      #strip /bin/
     2064    #first compile the cuda kernels
     2065    #(using the same cuda SDK for both nvenc modules for now..)
     2066    #TODO:
     2067    # * compile directly to output directory instead of using data files?
     2068    # * detect which arches we want to build for? (does it really matter much?)
     2069    kernels = ("ARGB_to_NV12", "ARGB_to_YUV444", "BGRA_to_NV12", "BGRA_to_YUV444")
     2070    for kernel in kernels:
     2071        cuda_src = "xpra/codecs/cuda_common/%s.cu" % kernel
     2072        cuda_bin = "xpra/codecs/cuda_common/%s.fatbin" % kernel
     2073        if os.path.exists(cuda_bin) and (cuda_rebuild_ENABLED is False):
     2074            continue
     2075        reason = should_rebuild(cuda_src, cuda_bin)
     2076        if not reason:
     2077            continue
     2078        print("rebuilding %s: %s" % (kernel, reason))
     2079        cmd = [nvcc,
     2080               '-fatbin',
     2081               #"-cubin",
     2082               #"-arch=compute_30", "-code=compute_30,sm_30,sm_35",
     2083               #"-gencode=arch=compute_50,code=sm_50",
     2084               #"-gencode=arch=compute_52,code=sm_52",
     2085               #"-gencode=arch=compute_52,code=compute_52",
     2086               "-c", cuda_src,
     2087               "-o", cuda_bin]
     2088        #GCC 8.1 has compatibility issues with CUDA 9.2,
     2089        #so revert to C++03:
     2090        if get_gcc_version()>=[8, 1]:
     2091            cmd.append("-std=c++03")
     2092        #GCC 6 uses C++11 by default:
     2093        elif get_gcc_version()>=[6, 0]:
     2094            cmd.append("-std=c++11")
     2095        CL_VERSION = os.environ.get("CL_VERSION")
     2096        if CL_VERSION:
     2097            cmd += ["--use-local-env", "--cl-version", CL_VERSION]
     2098            #-ccbin "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cl.exe"
     2099            cmd += ["--machine", "32"]
     2100        if WIN32:
     2101            #cmd += ["--compiler-bindir", "C:\\msys64\\mingw64\\bin\\g++.exe"]
     2102            #cmd += ["--input-drive-prefix", "/"]
     2103            #cmd += ["--dependency-drive-prefix", "/"]
     2104            cmd += ["-I%s" % os.path.abspath("win32")]
     2105        comp_code_options = [(35, 35)]
     2106        #see: http://docs.nvidia.com/cuda/maxwell-compatibility-guide/#building-maxwell-compatible-apps-using-cuda-6-0
     2107        if version!=(0,) and version<(7, 5):
     2108            print("CUDA version %s is very unlikely to work" % (version,))
     2109            print("try upgrading to version 7.5 or later")
     2110        if version>=(7, 5):
     2111            comp_code_options.append((50, 50))
     2112            comp_code_options.append((52, 52))
     2113            comp_code_options.append((53, 53))
     2114        if version>=(8, 0):
     2115            comp_code_options.append((60, 60))
     2116            comp_code_options.append((61, 61))
     2117            comp_code_options.append((62, 62))
     2118        if version>=(9, 0):
     2119            comp_code_options.append((70, 70))
     2120        if version>=(10, 0):
     2121            comp_code_options.append((75, 75))
     2122        if version>=(11, 0):
     2123            comp_code_options.append((80, 80))
     2124        if version>=(11, 1):
     2125            comp_code_options.append((86, 86))
     2126        for arch, code in comp_code_options:
     2127            cmd.append("-gencode=arch=compute_%s,code=sm_%s" % (arch, code))
     2128        print("CUDA compiling %s (%s)" % (kernel.ljust(16), reason))
     2129        print(" %s" % " ".join("'%s'" % x for x in cmd))
     2130        c, stdout, stderr = get_status_output(cmd)
     2131        if c!=0:
     2132            print("Error: failed to compile CUDA kernel %s" % kernel)
     2133            print(stdout or "")
     2134            print(stderr or "")
     2135            sys.exit(1)
     2136    CUDA_BIN = "%s/cuda" % share_xpra
     2137    add_data_files(CUDA_BIN, ["xpra/codecs/cuda_common/%s.fatbin" % x for x in kernels])
     2138
    11812139if 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))
     2140    nvencmodule = "nvenc"
     2141    nvenc_pkgconfig = pkgconfig(nvencmodule, ignored_flags=["-l", "-L"])
     2142    #make it possible to build against SDK v10
     2143    add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-error=deprecated-declarations")
     2144    #don't link against libnvidia-encode, we load it dynamically:
     2145    libraries = nvenc_pkgconfig.get("libraries", [])
     2146    if "nvidia-encode" in libraries:
     2147        libraries.remove("nvidia-encode")
     2148    if not cython_version_compare("0.29"):
     2149        #older versions emit spurious warnings:
     2150        print("Warning: using workaround for outdated version of cython")
     2151        add_to_keywords(nvenc_pkgconfig, 'extra_compile_args', "-Wno-error=sign-compare")
     2152    cython_add(Extension("xpra.codecs.%s.encoder" % nvencmodule,
     2153                         ["xpra/codecs/%s/encoder.pyx" % nvencmodule],
     2154                         **nvenc_pkgconfig))
    11872155
    11882156toggle_packages(enc_x264_ENABLED, "xpra.codecs.enc_x264")
    11892157if enc_x264_ENABLED:
    1190     x264_pkgconfig = pkgconfig("x264", static=x264_static_ENABLED)
     2158    x264_pkgconfig = pkgconfig("x264")
     2159    if get_gcc_version()>=[6, 0]:
     2160        add_to_keywords(x264_pkgconfig, 'extra_compile_args', "-Wno-unused-variable")
    11912161    cython_add(Extension("xpra.codecs.enc_x264.encoder",
    11922162                ["xpra/codecs/enc_x264/encoder.pyx"],
    1193                 **x264_pkgconfig), min_version=(0, 16))
     2163                **x264_pkgconfig))
    11942164
    11952165toggle_packages(enc_x265_ENABLED, "xpra.codecs.enc_x265")
    11962166if enc_x265_ENABLED:
    1197     x265_pkgconfig = pkgconfig("x265", static=x265_static_ENABLED)
     2167    x265_pkgconfig = pkgconfig("x265")
    11982168    cython_add(Extension("xpra.codecs.enc_x265.encoder",
    11992169                ["xpra/codecs/enc_x265/encoder.pyx"],
    1200                 **x265_pkgconfig), min_version=(0, 16))
     2170                **x265_pkgconfig))
     2171
     2172toggle_packages(pillow_ENABLED, "xpra.codecs.pillow")
     2173if pillow_ENABLED:
     2174    external_includes += ["PIL", "PIL.Image", "PIL.WebPImagePlugin"]
    12012175
    12022176toggle_packages(webp_ENABLED, "xpra.codecs.webp")
    12032177if webp_ENABLED:
    1204     webp_pkgconfig = pkgconfig("webp")
    1205     cython_add(Extension("xpra.codecs.webp.encode",
    1206                 ["xpra/codecs/webp/encode.pyx"],
    1207                 **webp_pkgconfig), min_version=(0, 16))
    1208 
    1209 toggle_packages(dec_avcodec_ENABLED, "xpra.codecs.dec_avcodec")
    1210 if dec_avcodec_ENABLED:
    1211     make_constants("xpra", "codecs", "dec_avcodec", "constants")
    1212     avcodec_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec_static_ENABLED)
    1213     cython_add(Extension("xpra.codecs.dec_avcodec.decoder",
    1214                 ["xpra/codecs/dec_avcodec/decoder.pyx", "xpra/codecs/memalign/memalign.c", "xpra/codecs/inline.c"],
    1215                 **avcodec_pkgconfig), min_version=(0, 19))
     2178    webp_pkgconfig = pkgconfig("libwebp")
     2179    cython_add(Extension("xpra.codecs.webp.encoder",
     2180                    ["xpra/codecs/webp/encoder.pyx"],
     2181                    **webp_pkgconfig))
     2182    cython_add(Extension("xpra.codecs.webp.decoder",
     2183                ["xpra/codecs/webp/decoder.pyx"],
     2184                **webp_pkgconfig))
     2185
     2186jpeg = jpeg_decoder_ENABLED or jpeg_encoder_ENABLED
     2187toggle_packages(jpeg, "xpra.codecs.jpeg")
     2188if jpeg:
     2189    if jpeg_encoder_ENABLED:
     2190        jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2191        if not pkg_config_version("1.4", "libturbojpeg"):
     2192            #older versions don't have const argument:
     2193            remove_from_keywords(jpeg_pkgconfig, 'extra_compile_args', "-Werror")
     2194        cython_add(Extension("xpra.codecs.jpeg.encoder",
     2195                ["xpra/codecs/jpeg/encoder.pyx"],
     2196                **jpeg_pkgconfig))
     2197    if jpeg_decoder_ENABLED:
     2198        jpeg_pkgconfig = pkgconfig("libturbojpeg")
     2199        cython_add(Extension("xpra.codecs.jpeg.decoder",
     2200                ["xpra/codecs/jpeg/decoder.pyx"],
     2201                **jpeg_pkgconfig))
     2202
     2203#swscale and avcodec2 use libav_common/av_log:
     2204libav_common = dec_avcodec2_ENABLED or csc_swscale_ENABLED
     2205toggle_packages(libav_common, "xpra.codecs.libav_common")
     2206if libav_common:
     2207    avutil_pkgconfig = pkgconfig("libavutil")
     2208    if get_gcc_version()>=[9, 0]:
     2209        add_to_keywords(avutil_pkgconfig, 'extra_compile_args', "-Wno-error=attributes")
     2210    cython_add(Extension("xpra.codecs.libav_common.av_log",
     2211                ["xpra/codecs/libav_common/av_log.pyx"],
     2212                **avutil_pkgconfig))
     2213
    12162214
    12172215toggle_packages(dec_avcodec2_ENABLED, "xpra.codecs.dec_avcodec2")
    12182216if dec_avcodec2_ENABLED:
    1219     avcodec2_pkgconfig = pkgconfig("avcodec", "avutil", static=avcodec2_static_ENABLED)
     2217    avcodec2_pkgconfig = pkgconfig("libavcodec", "libavutil", "libavformat")
     2218    if get_gcc_version()>=[9, 0]:
     2219        add_to_keywords(avcodec2_pkgconfig, 'extra_compile_args', "-Wno-error=attributes")
    12202220    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 
     2221                ["xpra/codecs/dec_avcodec2/decoder.pyx", "xpra/codecs/dec_avcodec2/register_compat.c"],
     2222                **avcodec2_pkgconfig))
     2223
     2224
     2225toggle_packages(csc_libyuv_ENABLED, "xpra.codecs.csc_libyuv")
     2226if csc_libyuv_ENABLED:
     2227    libyuv_pkgconfig = pkgconfig("libyuv")
     2228    cython_add(Extension("xpra.codecs.csc_libyuv.colorspace_converter",
     2229                ["xpra/codecs/csc_libyuv/colorspace_converter.pyx"],
     2230                language="c++",
     2231                **libyuv_pkgconfig))
    12242232
    12252233toggle_packages(csc_swscale_ENABLED, "xpra.codecs.csc_swscale")
    12262234if csc_swscale_ENABLED:
    1227     make_constants("xpra", "codecs", "csc_swscale", "constants")
    1228     swscale_pkgconfig = pkgconfig("swscale", static=swscale_static_ENABLED)
     2235    swscale_pkgconfig = pkgconfig("libswscale", "libavutil")
     2236    if get_gcc_version()>=[9, 0]:
     2237        add_to_keywords(swscale_pkgconfig, 'extra_compile_args', "-Wno-error=attributes")
    12292238    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))
     2239                ["xpra/codecs/csc_swscale/colorspace_converter.pyx"],
     2240                **swscale_pkgconfig))
    12322241
    12332242toggle_packages(csc_cython_ENABLED, "xpra.codecs.csc_cython")
    12342243if csc_cython_ENABLED:
    1235     csc_cython_pkgconfig = pkgconfig()
     2244    csc_cython_pkgconfig = pkgconfig(optimize=3)
    12362245    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))
     2246                         ["xpra/codecs/csc_cython/colorspace_converter.pyx"]+membuffers_c,
     2247                         **csc_cython_pkgconfig))
    12392248
    12402249toggle_packages(vpx_ENABLED, "xpra.codecs.vpx")
    12412250if vpx_ENABLED:
    1242     vpx_pkgconfig = pkgconfig("vpx", static=vpx_static_ENABLED)
     2251    vpx_pkgconfig = pkgconfig("vpx")
    12432252    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))
     2253                ["xpra/codecs/vpx/encoder.pyx"],
     2254                **vpx_pkgconfig))
    12462255    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))
     2256                ["xpra/codecs/vpx/decoder.pyx"],
     2257                **vpx_pkgconfig))
     2258
     2259toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
     2260if enc_ffmpeg_ENABLED:
     2261    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat", "libavutil")
     2262    if get_gcc_version()>=[9, 0]:
     2263        add_to_keywords(ffmpeg_pkgconfig, 'extra_compile_args', "-Wno-error=attributes")
     2264    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
     2265                ["xpra/codecs/enc_ffmpeg/encoder.pyx"],
     2266                **ffmpeg_pkgconfig))
     2267
     2268toggle_packages(v4l2_ENABLED, "xpra.codecs.v4l2")
     2269if v4l2_ENABLED:
     2270    v4l2_pkgconfig = pkgconfig()
     2271    #fugly warning: cython makes this difficult,
     2272    #we have to figure out if "device_caps" exists in the headers:
     2273    videodev2_h = "/usr/include/linux/videodev2.h"
     2274    constants_pxi = "xpra/codecs/v4l2/constants.pxi"
     2275    if not os.path.exists(videodev2_h) or should_rebuild(videodev2_h, constants_pxi):
     2276        ENABLE_DEVICE_CAPS = 0
     2277        if os.path.exists(videodev2_h):
     2278            with open(videodev2_h) as f:
     2279                hdata = f.read()
     2280            ENABLE_DEVICE_CAPS = int(hdata.find("device_caps")>=0)
     2281        with open(constants_pxi, "wb") as f:
     2282            f.write(b"DEF ENABLE_DEVICE_CAPS=%i" % ENABLE_DEVICE_CAPS)
     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 ImportError:
     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.