Best Python code snippet using tempest_python
swift.py
Source:swift.py  
1# Copyright 2010-2011 OpenStack Foundation2# All Rights Reserved.3#4#    Licensed under the Apache License, Version 2.0 (the "License"); you may5#    not use this file except in compliance with the License. You may obtain6#    a copy of the License at7#8#         http://www.apache.org/licenses/LICENSE-2.09#10#    Unless required by applicable law or agreed to in writing, software11#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT12#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the13#    License for the specific language governing permissions and limitations14#    under the License.15"""Storage backend for SWIFT"""16from __future__ import absolute_import17import hashlib18import httplib19import math20from oslo.config import cfg21import six.moves.urllib.parse as urlparse22from glance.common import auth23from glance.common import exception24from glance.openstack.common import excutils25import glance.openstack.common.log as logging26import glance.store27import glance.store.base28import glance.store.location29try:30    import swiftclient31except ImportError:32    pass33LOG = logging.getLogger(__name__)34DEFAULT_CONTAINER = 'glance'35DEFAULT_LARGE_OBJECT_SIZE = 5 * 1024  # 5GB36DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 200  # 200M37ONE_MB = 1000 * 102438swift_opts = [39    cfg.BoolOpt('swift_enable_snet', default=False,40                help=_('Whether to use ServiceNET to communicate with the '41                       'Swift storage servers.')),42    cfg.StrOpt('swift_store_auth_address',43               help=_('The address where the Swift authentication service '44                      'is listening.')),45    cfg.StrOpt('swift_store_user', secret=True,46               help=_('The user to authenticate against the Swift '47                      'authentication service.')),48    cfg.StrOpt('swift_store_key', secret=True,49               help=_('Auth key for the user authenticating against the '50                      'Swift authentication service.')),51    cfg.StrOpt('swift_store_auth_version', default='2',52               help=_('Version of the authentication service to use. '53                      'Valid versions are 2 for keystone and 1 for swauth '54                      'and rackspace.')),55    cfg.BoolOpt('swift_store_auth_insecure', default=False,56                help=_('If True, swiftclient won\'t check for a valid SSL '57                       'certificate when authenticating.')),58    cfg.StrOpt('swift_store_region',59               help=_('The region of the swift endpoint to be used for '60                      'single tenant. This setting is only necessary if the '61                      'tenant has multiple swift endpoints.')),62    cfg.StrOpt('swift_store_endpoint_type', default='publicURL',63               help=_('A string giving the endpoint type of the swift '64                      'service to use (publicURL, adminURL or internalURL). '65                      'This setting is only used if swift_store_auth_version '66                      'is 2.')),67    cfg.StrOpt('swift_store_service_type', default='object-store',68               help=_('A string giving the service type of the swift service '69                      'to use. This setting is only used if '70                      'swift_store_auth_version is 2.')),71    cfg.StrOpt('swift_store_container',72               default=DEFAULT_CONTAINER,73               help=_('Container within the account that the account should '74                      'use for storing images in Swift.')),75    cfg.IntOpt('swift_store_large_object_size',76               default=DEFAULT_LARGE_OBJECT_SIZE,77               help=_('The size, in MB, that Glance will start chunking image '78                      'files and do a large object manifest in Swift.')),79    cfg.IntOpt('swift_store_large_object_chunk_size',80               default=DEFAULT_LARGE_OBJECT_CHUNK_SIZE,81               help=_('The amount of data written to a temporary disk buffer '82                      'during the process of chunking the image file.')),83    cfg.BoolOpt('swift_store_create_container_on_put', default=False,84                help=_('A boolean value that determines if we create the '85                       'container if it does not exist.')),86    cfg.BoolOpt('swift_store_multi_tenant', default=False,87                help=_('If set to True, enables multi-tenant storage '88                       'mode which causes Glance images to be stored in '89                       'tenant specific Swift accounts.')),90    cfg.ListOpt('swift_store_admin_tenants', default=[],91                help=_('A list of tenants that will be granted read/write '92                       'access on all Swift containers created by Glance in '93                       'multi-tenant mode.')),94    cfg.BoolOpt('swift_store_ssl_compression', default=True,95                help=_('If set to False, disables SSL layer compression of '96                       'https swift requests. Setting to False may improve '97                       'performance for images which are already in a '98                       'compressed format, eg qcow2.')),99    cfg.IntOpt('swift_store_retry_get_count', default=0,100               help=_('The number of times a Swift download will be retried '101                      'before the request fails.'))102]103CONF = cfg.CONF104CONF.register_opts(swift_opts)105def swift_retry_iter(resp_iter, length, store, location):106    length = length if length else (resp_iter.len107                                    if hasattr(resp_iter, 'len') else 0)108    retries = 0109    bytes_read = 0110    while retries <= CONF.swift_store_retry_get_count:111        try:112            for chunk in resp_iter:113                yield chunk114                bytes_read += len(chunk)115        except swiftclient.ClientException as e:116            LOG.warn(_("Swift exception raised %s") % e)117        if bytes_read != length:118            if retries == CONF.swift_store_retry_get_count:119                # terminate silently and let higher level decide120                LOG.error(_("Stopping Swift retries after %d "121                            "attempts") % retries)122                break123            else:124                retries += 1125                LOG.info(_("Retrying Swift connection "126                           "(%(retries)d/%(max_retries)d) with "127                           "range=%(start)d-%(end)d") %128                         {'retries': retries,129                          'max_retries': CONF.swift_store_retry_get_count,130                          'start': bytes_read,131                          'end': length})132                (resp_headers, resp_iter) = store._get_object(location, None,133                                                              bytes_read)134        else:135            break136class StoreLocation(glance.store.location.StoreLocation):137    """138    Class describing a Swift URI. A Swift URI can look like any of139    the following:140        swift://user:pass@authurl.com/container/obj-id141        swift://account:user:pass@authurl.com/container/obj-id142        swift+http://user:pass@authurl.com/container/obj-id143        swift+https://user:pass@authurl.com/container/obj-id144    When using multi-tenant a URI might look like this (a storage URL):145        swift+https://example.com/container/obj-id146    The swift+http:// URIs indicate there is an HTTP authentication URL.147    The default for Swift is an HTTPS authentication URL, so swift:// and148    swift+https:// are the same...149    """150    def process_specs(self):151        self.scheme = self.specs.get('scheme', 'swift+https')152        self.user = self.specs.get('user')153        self.key = self.specs.get('key')154        self.auth_or_store_url = self.specs.get('auth_or_store_url')155        self.container = self.specs.get('container')156        self.obj = self.specs.get('obj')157    def _get_credstring(self):158        if self.user and self.key:159            return '%s:%s@' % (urlparse.quote(self.user),160                               urlparse.quote(self.key))161        return ''162    def get_uri(self):163        auth_or_store_url = self.auth_or_store_url164        if auth_or_store_url.startswith('http://'):165            auth_or_store_url = auth_or_store_url[len('http://'):]166        elif auth_or_store_url.startswith('https://'):167            auth_or_store_url = auth_or_store_url[len('https://'):]168        credstring = self._get_credstring()169        auth_or_store_url = auth_or_store_url.strip('/')170        container = self.container.strip('/')171        obj = self.obj.strip('/')172        return '%s://%s%s/%s/%s' % (self.scheme, credstring, auth_or_store_url,173                                    container, obj)174    def parse_uri(self, uri):175        """176        Parse URLs. This method fixes an issue where credentials specified177        in the URL are interpreted differently in Python 2.6.1+ than prior178        versions of Python. It also deals with the peculiarity that new-style179        Swift URIs have where a username can contain a ':', like so:180            swift://account:user:pass@authurl.com/container/obj181        """182        # Make sure that URIs that contain multiple schemes, such as:183        # swift://user:pass@http://authurl.com/v1/container/obj184        # are immediately rejected.185        if uri.count('://') != 1:186            reason = _("URI cannot contain more than one occurrence "187                       "of a scheme. If you have specified a URI like "188                       "swift://user:pass@http://authurl.com/v1/container/obj"189                       ", you need to change it to use the "190                       "swift+http:// scheme, like so: "191                       "swift+http://user:pass@authurl.com/v1/container/obj")192            LOG.debug(_("Invalid store URI: %(reason)s"), {'reason': reason})193            raise exception.BadStoreUri(message=reason)194        pieces = urlparse.urlparse(uri)195        assert pieces.scheme in ('swift', 'swift+http', 'swift+https')196        self.scheme = pieces.scheme197        netloc = pieces.netloc198        path = pieces.path.lstrip('/')199        if netloc != '':200            # > Python 2.6.1201            if '@' in netloc:202                creds, netloc = netloc.split('@')203            else:204                creds = None205        else:206            # Python 2.6.1 compat207            # see lp659445 and Python issue7904208            if '@' in path:209                creds, path = path.split('@')210            else:211                creds = None212            netloc = path[0:path.find('/')].strip('/')213            path = path[path.find('/'):].strip('/')214        if creds:215            cred_parts = creds.split(':')216            if len(cred_parts) != 2:217                reason = (_("Badly formed credentials in Swift URI."))218                LOG.debug(reason)219                raise exception.BadStoreUri()220            user, key = cred_parts221            self.user = urlparse.unquote(user)222            self.key = urlparse.unquote(key)223        else:224            self.user = None225            self.key = None226        path_parts = path.split('/')227        try:228            self.obj = path_parts.pop()229            self.container = path_parts.pop()230            if not netloc.startswith('http'):231                # push hostname back into the remaining to build full authurl232                path_parts.insert(0, netloc)233                self.auth_or_store_url = '/'.join(path_parts)234        except IndexError:235            reason = _("Badly formed Swift URI.")236            LOG.debug(reason)237            raise exception.BadStoreUri()238    @property239    def swift_url(self):240        """241        Creates a fully-qualified auth url that the Swift client library can242        use. The scheme for the auth_url is determined using the scheme243        included in the `location` field.244        HTTPS is assumed, unless 'swift+http' is specified.245        """246        if self.auth_or_store_url.startswith('http'):247            return self.auth_or_store_url248        else:249            if self.scheme in ('swift+https', 'swift'):250                auth_scheme = 'https://'251            else:252                auth_scheme = 'http://'253            return ''.join([auth_scheme, self.auth_or_store_url])254def Store(context=None, loc=None):255    if (CONF.swift_store_multi_tenant and256            (loc is None or loc.store_location.user is None)):257        return MultiTenantStore(context, loc)258    return SingleTenantStore(context, loc)259class BaseStore(glance.store.base.Store):260    CHUNKSIZE = 65536261    def get_schemes(self):262        return ('swift+https', 'swift', 'swift+http')263    def configure(self):264        _obj_size = self._option_get('swift_store_large_object_size')265        self.large_object_size = _obj_size * ONE_MB266        _chunk_size = self._option_get('swift_store_large_object_chunk_size')267        self.large_object_chunk_size = _chunk_size * ONE_MB268        self.admin_tenants = CONF.swift_store_admin_tenants269        self.region = CONF.swift_store_region270        self.service_type = CONF.swift_store_service_type271        self.endpoint_type = CONF.swift_store_endpoint_type272        self.snet = CONF.swift_enable_snet273        self.insecure = CONF.swift_store_auth_insecure274        self.ssl_compression = CONF.swift_store_ssl_compression275    def _get_object(self, location, connection=None, start=None):276        if not connection:277            connection = self.get_connection(location)278        headers = {}279        if start is not None:280            bytes_range = 'bytes=%d-' % start281            headers = {'Range': bytes_range}282        try:283            resp_headers, resp_body = connection.get_object(284                container=location.container, obj=location.obj,285                resp_chunk_size=self.CHUNKSIZE, headers=headers)286        except swiftclient.ClientException as e:287            if e.http_status == httplib.NOT_FOUND:288                msg = _("Swift could not find object %s.") % location.obj289                LOG.warn(msg)290                raise exception.NotFound(msg)291            else:292                raise293        return (resp_headers, resp_body)294    def get(self, location, connection=None):295        location = location.store_location296        (resp_headers, resp_body) = self._get_object(location, connection)297        class ResponseIndexable(glance.store.Indexable):298            def another(self):299                try:300                    return self.wrapped.next()301                except StopIteration:302                    return ''303        length = int(resp_headers.get('content-length', 0))304        if CONF.swift_store_retry_get_count > 0:305            resp_body = swift_retry_iter(resp_body, length, self, location)306        return (ResponseIndexable(resp_body, length), length)307    def get_size(self, location, connection=None):308        location = location.store_location309        if not connection:310            connection = self.get_connection(location)311        try:312            resp_headers = connection.head_object(313                container=location.container, obj=location.obj)314            return int(resp_headers.get('content-length', 0))315        except Exception:316            return 0317    def _option_get(self, param):318        result = getattr(CONF, param)319        if not result:320            reason = (_("Could not find %(param)s in configuration "321                        "options.") % {'param': param})322            LOG.error(reason)323            raise exception.BadStoreConfiguration(store_name="swift",324                                                  reason=reason)325        return result326    def _delete_stale_chunks(self, connection, container, chunk_list):327        for chunk in chunk_list:328            LOG.debug(_("Deleting chunk %s") % chunk)329            try:330                connection.delete_object(container, chunk)331            except Exception:332                msg = _("Failed to delete orphaned chunk "333                        "%(container)s/%(chunk)s")334                LOG.exception(msg % {'container': container,335                                     'chunk': chunk})336    def add(self, image_id, image_file, image_size, connection=None):337        location = self.create_location(image_id)338        if not connection:339            connection = self.get_connection(location)340        self._create_container_if_missing(location.container, connection)341        LOG.debug(_("Adding image object '%(obj_name)s' "342                    "to Swift") % dict(obj_name=location.obj))343        try:344            if image_size > 0 and image_size < self.large_object_size:345                # Image size is known, and is less than large_object_size.346                # Send to Swift with regular PUT.347                obj_etag = connection.put_object(location.container,348                                                 location.obj, image_file,349                                                 content_length=image_size)350            else:351                # Write the image into Swift in chunks.352                chunk_id = 1353                if image_size > 0:354                    total_chunks = str(int(355                        math.ceil(float(image_size) /356                                  float(self.large_object_chunk_size))))357                else:358                    # image_size == 0 is when we don't know the size359                    # of the image. This can occur with older clients360                    # that don't inspect the payload size.361                    LOG.debug(_("Cannot determine image size. Adding as a "362                                "segmented object to Swift."))363                    total_chunks = '?'364                checksum = hashlib.md5()365                written_chunks = []366                combined_chunks_size = 0367                while True:368                    chunk_size = self.large_object_chunk_size369                    if image_size == 0:370                        content_length = None371                    else:372                        left = image_size - combined_chunks_size373                        if left == 0:374                            break375                        if chunk_size > left:376                            chunk_size = left377                        content_length = chunk_size378                    chunk_name = "%s-%05d" % (location.obj, chunk_id)379                    reader = ChunkReader(image_file, checksum, chunk_size)380                    try:381                        chunk_etag = connection.put_object(382                            location.container, chunk_name, reader,383                            content_length=content_length)384                        written_chunks.append(chunk_name)385                    except Exception:386                        # Delete orphaned segments from swift backend387                        with excutils.save_and_reraise_exception():388                            LOG.exception(_("Error during chunked upload to "389                                            "backend, deleting stale chunks"))390                            self._delete_stale_chunks(connection,391                                                      location.container,392                                                      written_chunks)393                    bytes_read = reader.bytes_read394                    msg = (_("Wrote chunk %(chunk_name)s (%(chunk_id)d/"395                             "%(total_chunks)s) of length %(bytes_read)d "396                             "to Swift returning MD5 of content: "397                             "%(chunk_etag)s") %398                           {'chunk_name': chunk_name,399                            'chunk_id': chunk_id,400                            'total_chunks': total_chunks,401                            'bytes_read': bytes_read,402                            'chunk_etag': chunk_etag})403                    LOG.debug(msg)404                    if bytes_read == 0:405                        # Delete the last chunk, because it's of zero size.406                        # This will happen if size == 0.407                        LOG.debug(_("Deleting final zero-length chunk"))408                        connection.delete_object(location.container,409                                                 chunk_name)410                        break411                    chunk_id += 1412                    combined_chunks_size += bytes_read413                # In the case we have been given an unknown image size,414                # set the size to the total size of the combined chunks.415                if image_size == 0:416                    image_size = combined_chunks_size417                # Now we write the object manifest and return the418                # manifest's etag...419                manifest = "%s/%s-" % (location.container, location.obj)420                headers = {'ETag': hashlib.md5("").hexdigest(),421                           'X-Object-Manifest': manifest}422                # The ETag returned for the manifest is actually the423                # MD5 hash of the concatenated checksums of the strings424                # of each chunk...so we ignore this result in favour of425                # the MD5 of the entire image file contents, so that426                # users can verify the image file contents accordingly427                connection.put_object(location.container, location.obj,428                                      None, headers=headers)429                obj_etag = checksum.hexdigest()430            # NOTE: We return the user and key here! Have to because431            # location is used by the API server to return the actual432            # image data. We *really* should consider NOT returning433            # the location attribute from GET /images/<ID> and434            # GET /images/details435            return (location.get_uri(), image_size, obj_etag, {})436        except swiftclient.ClientException as e:437            if e.http_status == httplib.CONFLICT:438                raise exception.Duplicate(_("Swift already has an image at "439                                            "this location"))440            msg = (_("Failed to add object to Swift.\n"441                     "Got error from Swift: %(e)s") % {'e': e})442            LOG.error(msg)443            raise glance.store.BackendException(msg)444    def delete(self, location, connection=None):445        location = location.store_location446        if not connection:447            connection = self.get_connection(location)448        try:449            # We request the manifest for the object. If one exists,450            # that means the object was uploaded in chunks/segments,451            # and we need to delete all the chunks as well as the452            # manifest.453            manifest = None454            try:455                headers = connection.head_object(456                    location.container, location.obj)457                manifest = headers.get('x-object-manifest')458            except swiftclient.ClientException as e:459                if e.http_status != httplib.NOT_FOUND:460                    raise461            if manifest:462                # Delete all the chunks before the object manifest itself463                obj_container, obj_prefix = manifest.split('/', 1)464                segments = connection.get_container(465                    obj_container, prefix=obj_prefix)[1]466                for segment in segments:467                    # TODO(jaypipes): This would be an easy area to parallelize468                    # since we're simply sending off parallelizable requests469                    # to Swift to delete stuff. It's not like we're going to470                    # be hogging up network or file I/O here...471                    connection.delete_object(obj_container,472                                             segment['name'])473            # Delete object (or, in segmented case, the manifest)474            connection.delete_object(location.container, location.obj)475        except swiftclient.ClientException as e:476            if e.http_status == httplib.NOT_FOUND:477                msg = _("Swift could not find image at URI.")478                raise exception.NotFound(msg)479            else:480                raise481    def _create_container_if_missing(self, container, connection):482        """483        Creates a missing container in Swift if the484        ``swift_store_create_container_on_put`` option is set.485        :param container: Name of container to create486        :param connection: Connection to swift service487        """488        try:489            connection.head_container(container)490        except swiftclient.ClientException as e:491            if e.http_status == httplib.NOT_FOUND:492                if CONF.swift_store_create_container_on_put:493                    try:494                        connection.put_container(container)495                    except swiftclient.ClientException as e:496                        msg = (_("Failed to add container to Swift.\n"497                                 "Got error from Swift: %(e)s") % {'e': e})498                        raise glance.store.BackendException(msg)499                else:500                    msg = (_("The container %(container)s does not exist in "501                             "Swift. Please set the "502                             "swift_store_create_container_on_put option"503                             "to add container to Swift automatically.") %504                           {'container': container})505                    raise glance.store.BackendException(msg)506            else:507                raise508    def get_connection(self):509        raise NotImplemented()510    def create_location(self):511        raise NotImplemented()512class SingleTenantStore(BaseStore):513    EXAMPLE_URL = "swift://<USER>:<KEY>@<AUTH_ADDRESS>/<CONTAINER>/<FILE>"514    def configure(self):515        super(SingleTenantStore, self).configure()516        self.auth_version = self._option_get('swift_store_auth_version')517    def configure_add(self):518        self.auth_address = self._option_get('swift_store_auth_address')519        if self.auth_address.startswith('http://'):520            self.scheme = 'swift+http'521        else:522            self.scheme = 'swift+https'523        self.container = CONF.swift_store_container524        self.user = self._option_get('swift_store_user')525        self.key = self._option_get('swift_store_key')526    def create_location(self, image_id):527        specs = {'scheme': self.scheme,528                 'container': self.container,529                 'obj': str(image_id),530                 'auth_or_store_url': self.auth_address,531                 'user': self.user,532                 'key': self.key}533        return StoreLocation(specs)534    def get_connection(self, location):535        if not location.user:536            reason = (_("Location is missing user:password information."))537            LOG.debug(reason)538            raise exception.BadStoreUri(message=reason)539        auth_url = location.swift_url540        if not auth_url.endswith('/'):541            auth_url += '/'542        if self.auth_version == '2':543            try:544                tenant_name, user = location.user.split(':')545            except ValueError:546                reason = (_("Badly formed tenant:user '%(user)s' in "547                            "Swift URI") % {'user': location.user})548                LOG.debug(reason)549                raise exception.BadStoreUri()550        else:551            tenant_name = None552            user = location.user553        os_options = {}554        if self.region:555            os_options['region_name'] = self.region556        os_options['endpoint_type'] = self.endpoint_type557        os_options['service_type'] = self.service_type558        return swiftclient.Connection(559            auth_url, user, location.key, insecure=self.insecure,560            tenant_name=tenant_name, snet=self.snet,561            auth_version=self.auth_version, os_options=os_options,562            ssl_compression=self.ssl_compression)563class MultiTenantStore(BaseStore):564    EXAMPLE_URL = "swift://<SWIFT_URL>/<CONTAINER>/<FILE>"565    def configure_add(self):566        self.container = CONF.swift_store_container567        if self.context is None:568            reason = _("Multi-tenant Swift storage requires a context.")569            raise exception.BadStoreConfiguration(store_name="swift",570                                                  reason=reason)571        if self.context.service_catalog is None:572            reason = _("Multi-tenant Swift storage requires "573                       "a service catalog.")574            raise exception.BadStoreConfiguration(store_name="swift",575                                                  reason=reason)576        self.storage_url = auth.get_endpoint(577            self.context.service_catalog, service_type=self.service_type,578            endpoint_region=self.region, endpoint_type=self.endpoint_type)579        if self.storage_url.startswith('http://'):580            self.scheme = 'swift+http'581        else:582            self.scheme = 'swift+https'583    def delete(self, location, connection=None):584        if not connection:585            connection = self.get_connection(location.store_location)586        super(MultiTenantStore, self).delete(location, connection)587        connection.delete_container(location.store_location.container)588    def set_acls(self, location, public=False, read_tenants=None,589                 write_tenants=None, connection=None):590        location = location.store_location591        if not connection:592            connection = self.get_connection(location)593        if read_tenants is None:594            read_tenants = []595        if write_tenants is None:596            write_tenants = []597        headers = {}598        if public:599            headers['X-Container-Read'] = ".r:*,.rlistings"600        elif read_tenants:601            headers['X-Container-Read'] = ','.join('%s:*' % i602                                                   for i in read_tenants)603        else:604            headers['X-Container-Read'] = ''605        write_tenants.extend(self.admin_tenants)606        if write_tenants:607            headers['X-Container-Write'] = ','.join('%s:*' % i608                                                    for i in write_tenants)609        else:610            headers['X-Container-Write'] = ''611        try:612            connection.post_container(location.container, headers=headers)613        except swiftclient.ClientException as e:614            if e.http_status == httplib.NOT_FOUND:615                msg = _("Swift could not find image at URI.")616                raise exception.NotFound(msg)617            else:618                raise619    def create_location(self, image_id):620        specs = {'scheme': self.scheme,621                 'container': self.container + '_' + str(image_id),622                 'obj': str(image_id),623                 'auth_or_store_url': self.storage_url}624        return StoreLocation(specs)625    def get_connection(self, location):626        return swiftclient.Connection(627            None, self.context.user, None,628            preauthurl=location.swift_url,629            preauthtoken=self.context.auth_tok,630            tenant_name=self.context.tenant,631            auth_version='2', snet=self.snet, insecure=self.insecure,632            ssl_compression=self.ssl_compression)633class ChunkReader(object):634    def __init__(self, fd, checksum, total):635        self.fd = fd636        self.checksum = checksum637        self.total = total638        self.bytes_read = 0639    def read(self, i):640        left = self.total - self.bytes_read641        if i > left:642            i = left643        result = self.fd.read(i)644        self.bytes_read += len(result)645        self.checksum.update(result)...compiler_swift.py
Source:compiler_swift.py  
1import re2import string3import os.path4from waflib import Utils5from distutils.version import StrictVersion6def __run(cmd):7    try:8        output = Utils.subprocess.check_output(cmd, stderr=Utils.subprocess.STDOUT, universal_newlines=True)9        return output.strip()10    except Exception:11        return ""12def __add_swift_flags(ctx):13    ctx.env.SWIFT_FLAGS = [14        "-frontend", "-c", "-sdk", ctx.env.MACOS_SDK,15        "-enable-objc-interop", "-emit-objc-header", "-parse-as-library",16        "-target", "x86_64-apple-macosx10.10"17    ]18    verRe = re.compile("(?i)version\s?([\d.]+)")19    ctx.env.SWIFT_VERSION = verRe.search(__run([ctx.env.SWIFT, '-version'])).group(1)20    # prevent possible breakages with future swift versions21    if StrictVersion(ctx.env.SWIFT_VERSION) >= StrictVersion("6.0"):22        ctx.env.SWIFT_FLAGS.extend(["-swift-version", "5"])23    if ctx.is_debug_build():24        ctx.env.SWIFT_FLAGS.append("-g")25    if ctx.is_optimization():26        ctx.env.SWIFT_FLAGS.append("-O")27def __add_static_swift_library_linking_flags(ctx, swift_library):28    ctx.env.append_value('LINKFLAGS', [29        '-L%s' % swift_library,30        '-Xlinker', '-force_load_swift_libs', '-lc++',31    ])32def __add_dynamic_swift_library_linking_flags(ctx, swift_library):33    ctx.env.append_value('LINKFLAGS', ['-L%s' % swift_library])34    # ABI compatibility35    if StrictVersion(ctx.env.SWIFT_VERSION) >= StrictVersion("5.0"):36        ctx.env.append_value('LINKFLAGS', [37            '-Xlinker', '-rpath', '-Xlinker', '/usr/lib/swift',38            '-L/usr/lib/swift',39        ])40    ctx.env.append_value('LINKFLAGS', [41        '-Xlinker', '-rpath', '-Xlinker', swift_library,42    ])43def __find_swift_library(ctx):44    swift_libraries = {}45    # look for set lib paths in passed environment variables46    if 'SWIFT_LIB_DYNAMIC' in ctx.environ:47        swift_libraries['SWIFT_LIB_DYNAMIC'] = ctx.environ['SWIFT_LIB_DYNAMIC']48    if 'SWIFT_LIB_STATIC' in ctx.environ:49        swift_libraries['SWIFT_LIB_STATIC'] = ctx.environ['SWIFT_LIB_STATIC']50    # search for swift libs relative to the swift compiler executable51    swift_library_relative_paths = {52        'SWIFT_LIB_DYNAMIC': '../../lib/swift/macosx',53        'SWIFT_LIB_STATIC': '../../lib/swift_static/macosx'54    }55    for lib_type, path in swift_library_relative_paths.items():56        if lib_type not in swift_libraries:57            lib_path = os.path.join(ctx.env.SWIFT, path)58            swift_library = ctx.root.find_dir(lib_path)59            if swift_library is not None:60                swift_libraries[lib_type] = swift_library.abspath()61    # fall back to xcode-select path62    swift_library_paths = {63        'SWIFT_LIB_DYNAMIC': [64            'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx',65            'usr/lib/swift/macosx'66        ],67        'SWIFT_LIB_STATIC': [68            'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static/macosx',69            'usr/lib/swift_static/macosx'70        ]71    }72    dev_path = __run(['xcode-select', '-p'])[1:]73    for lib_type, paths in swift_library_paths.items():74        for path in paths:75            if lib_type not in swift_libraries:76                swift_library = ctx.root.find_dir([dev_path, path])77                if swift_library is not None:78                    swift_libraries[lib_type] = swift_library.abspath()79                    break80            else:81                break82    # check if library paths were found83    ctx.start_msg('Checking for dynamic Swift Library')84    if 'SWIFT_LIB_DYNAMIC' in swift_libraries:85        ctx.end_msg(swift_libraries['SWIFT_LIB_DYNAMIC'])86    else:87        ctx.end_msg(False)88    ctx.start_msg('Checking for static Swift Library')89    if 'SWIFT_LIB_STATIC' in swift_libraries:90        ctx.end_msg(swift_libraries['SWIFT_LIB_STATIC'])91        ctx.env['SWIFT_LIB_STATIC'] = swift_libraries['SWIFT_LIB_STATIC']92    else:93        ctx.end_msg(False)94    enableStatic = getattr(ctx.options, 'enable_swift-static')95    if (enableStatic) and 'SWIFT_LIB_STATIC' in swift_libraries:96        __add_static_swift_library_linking_flags(ctx, swift_libraries['SWIFT_LIB_STATIC'])97    else:98        __add_dynamic_swift_library_linking_flags(ctx, swift_libraries['SWIFT_LIB_DYNAMIC'])99def __find_macos_sdk(ctx):100    ctx.start_msg('Checking for macOS SDK')101    sdk = None102    sdk_build_version = None103    sdk_version = None104    # look for set macOS SDK paths and version in passed environment variables105    if 'MACOS_SDK' in ctx.environ:106        sdk = ctx.environ['MACOS_SDK']107    if 'MACOS_SDK_VERSION' in ctx.environ:108        ctx.env.MACOS_SDK_VERSION = ctx.environ['MACOS_SDK_VERSION']109    # find macOS SDK paths and version110    if not sdk:111        sdk = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-path'])112    if not ctx.env.MACOS_SDK_VERSION:113        # show-sdk-build-version: is not available on older command line tools, but return a build version (eg 17A360)114        # show-sdk-version: is always available, but on older dev tools it's only the major version115        sdk_build_version = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-build-version'])116        sdk_version = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-version'])117    if sdk:118        ctx.env.MACOS_SDK = sdk119        build_version = '10.10.0'120        if not ctx.env.MACOS_SDK_VERSION:121            # convert build version to a version string122            # first 2 two digits are the major version, starting with 15 which is 10.11 (offset of 4)123            # 1 char is the minor version, A => 0, B => 1 and ongoing124            # las digits are bugfix version, which are nor relevant for us125            # eg 16E185 => 10.12.4, 17A360 => 10.13, 18B71 => 10.14.1126            if sdk_build_version and isinstance(sdk_build_version, str):127                verRe = re.compile("(\d+)(\D+)(\d+)")128                version_parts = verRe.search(sdk_build_version)129                major = int(version_parts.group(1)) - 4130                minor = string.ascii_lowercase.index(version_parts.group(2).lower())131                build_version = '10.' + str(major) + '.' + str(minor)132            if not isinstance(sdk_version, str):133                sdk_version = '10.10.0'134            # pick the higher version, always pick sdk over build if newer135            if StrictVersion(build_version) > StrictVersion(sdk_version):136                ctx.env.MACOS_SDK_VERSION = build_version137            else:138                ctx.env.MACOS_SDK_VERSION = sdk_version139        ctx.end_msg(sdk + ' (version found: ' + ctx.env.MACOS_SDK_VERSION + ')')140    else:141        ctx.end_msg(False)142def __find_swift_compiler(ctx):143    ctx.start_msg('Checking for swift (Swift compiler)')144    swift = ''145    # look for set swift paths in passed environment variables146    if 'SWIFT' in ctx.environ:147        swift = ctx.environ['SWIFT']148    # find swift executable149    if not swift:150        swift = __run(['xcrun', '-find', 'swift'])151    if swift:152        ctx.end_msg(swift)153        ctx.env.SWIFT = swift154        __add_swift_flags(ctx)155        __find_swift_library(ctx)156    else:157        ctx.end_msg(False)158def configure(ctx):159    if ctx.env.DEST_OS == "darwin":160        __find_macos_sdk(ctx)161        if ctx.options.enable_swift is not False:...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
