示例#1
0
    def process_rspecs( self ):
        #
        # Look for RSpecs at the root of the view. Multiple RSpecs are
        # prohibited unless one of them ends up importing all of the others
        # (directly or indirectly).
        #

        # Collect all rspecs
        rspec_path_list = list()
        root_files = os.listdir( self.localPath )
        rspec = None
        for f in root_files:
            root, ext = os.path.splitext( f )
            if ext.lower() == '.rspec':
                rspec_path = os.path.join(self.getRoot(), f )
		if rspec == None:
                    rspec = RSpecFile( rspec_path, parent=None, view=self, wipe_cache=True)
		else:
                    userErrorExit("Only one rspec is allowed at the root of the view.")
			
        if rspec == None:
            userErrorExit("No RSpec found in view")

        infoMessage("Using RSpec: '%s'"%(rspec.getFilename()), 2)
        self.setRSpec( rspec )
    def getURLFromWorkingCopy( self, lc_path ):
        if not os.path.exists(lc_path):
            userErrorExit("Given path to local copy '%s' is invalid"%(lc_path))

        entry = self.client.info( lc_path )
        #strip the %20-quotations from the url
        return urllib.unquote( entry['url'] ) if entry != None else None
示例#3
0
def createRepoFromRCS( rcs, id, path, href, rev ):

    rcs = rcs.lower() if rcs != None else None

    if rcs == 'svn':
        return CTXRepositorySVN( id, path, href, rev )
    elif rcs == None or rcs =='':
        return CTXRepositoryFS( id, path, href, rev )
    else:
        userErrorExit("Unsupported RCS for repository '%s'"%(id))
示例#4
0
    def process_rspecs( self ):
        #
        # Look for RSpecs at the root of the view. Multiple RSpecs are
        # prohibited unless one of them ends up importing all of the others
        # (directly or indirectly).
        #

        # Collect all rspecs
        rspec_path_list = list()
        root_files = os.listdir( self.localPath )
        for f in root_files:
            root, ext = os.path.splitext( f )
            if ext.lower() == '.rspec':
                rspec_path_list.append( os.path.join(self.getRoot(), f ) )

        if len(rspec_path_list) != 0:
            infoMessage("RSpecs detected in view: \n   %s"%("\n   ".join(rspec_path_list)), 2)

        # Determine the import relationship between the RSpecs
        if len(rspec_path_list):
            #
            candidate_rspecs = list()
            for rspec_path in rspec_path_list:

                rspec = RSpecFile( rspec_path, parent=None, view=self )
                remainder = False

                for potential_import in rspec_path_list:
                    if potential_import == rspec_path:
                        continue # skip checking if we import ourselves

                    if potential_import not in rspec.getImportPaths( recursive=True ):
                        remainder = True
                        break # we found an RSpec not imported so move on to the next one

                # remainder is False if all other RSpecs could be found in the
                # import tree of 'rspec', or if 'rspec' is the only one present
                # in the view.
                if remainder == False:
                    candidate_rspecs.append( rspec )

            # We should have exactly ONE candidate rspec. If we have zero candidates, it
            # means no single rspec imports all the others.
            # If we have multiple candidates, it means more than one rspec imports all
            # the others. Both these cases are terminal errors since we have no logic
            # way of selecting one of the candidates.
            if len(candidate_rspecs) != 1:
                userErrorExit("RSpec selection is ambiguous. Only one RSpec is allowed in a view. If multiple RSpecs exist, at least one of them must directly or indirectly import the others")
            else:
                infoMessage("Using RSpec: '%s'"%(candidate_rspecs[0].getFilename()), 2)
                self.setRSpec( candidate_rspecs[0] )

        else:
            infoMessage("No RSpec found in view", 2)
    def getRevisionFromWorkingCopy( self, lc_path ):
        if not os.path.exists(lc_path):
            userErrorExit("Given path to local copy '%s' is invalid"%(lc_path))

        entry = self.client.info( lc_path )

        if entry.revision.kind == pysvn.opt_revision_kind.head:
            return 'HEAD'
        elif entry.revision.kind == pysvn.opt_revision_kind.number:
            return entry.revision.number
        else:
            ctxAssert( False, "Unhandled pysvn revision kind" )
示例#6
0
def locate_git():
    warn_hardcoded_git_path = False
    for git_cand in ['git', 'git.cmd', 'git.exe', 'git.bat']:
        for path in os.environ["PATH"].split(os.pathsep):
            if os.path.exists(os.path.join( path, git_cand)) and os.access( os.path.join( path, git_cand), os.X_OK):
                return git_cand
    for git_cand in ['git', 'git.cmd', 'git.exe', 'git.bat']:
        for path in ['C:\\Program Files\\Git\cmd', 'C:\\Program Files (x86)\\Git\\cmd', '/usr/bin', '/usr/local/bin']:
            if os.path.exists(os.path.join( path, git_cand)) and os.access( os.path.join( path, git_cand), os.X_OK):
                #warningMessage("Falling back to hardcoded git path")
                return os.path.join(path, git_cand)
 
    userErrorExit("Git cannot be found in your PATH. Please re-install Git and make sure the git.cmd, git.exe or git binary can be found in your PATH")
 def isWorkingCopy( self, local_path ):
     try:
         entry = self.client.info( local_path )
     except pysvn.ClientError, e:
         for message, code in e.args[1]:
             if code in [ERR_NOT_A_WORKING_COPY, ERR_INVALID_ARGUMENT]:
                 return False
             elif code == ERR_UNSUPPORTED_WORKING_COPY_FORMAT:
                 userErrorExit("Unsupported working copy format in '%s'\n \
                     Looks like the working copy was accessed using an incomptatible version of a svn client\n. \
                     PySvn currently uses svn version %s. "%(local_path, pysvn.svn_version)  )
             else:
                 ctxAssert( False, "Unhandled pysvn exception: (%d) %s. This code need to be updated." )
示例#8
0
    def __init__(self, id_name, local_path, href, rev):
        self.client = ctx_svn_client.CTXSubversionClient()
        self.path = os.path.join(local_path, id_name)

        if href == None:
            # Try to get href from local copy
            href = self.client.getURLFromWorkingCopy( self.path )

        if href == None:
            userErrorExit("No HREF specified for repository '%s'. Failed to aquire HREF from local copy '%s'"\
                           %(id_name, self.path))

        CTXRepository.__init__( self, id_name, local_path, href, rev, version_control=True )
        self.msgSender = "CTXRepositorySVN"
示例#9
0
    def __init__(self, id_name, local_path, href, rev):
        self.path = os.path.join(local_path, id_name)

        if href == None:
            # No remote repo source given, see if we have a local representation
            if os.path.exists( self.path ):
                href = self.path

        if href == None:
            userErrorExit("No HREF specified for repository '%s'. Failed to aquire HREF from local copy '%s'"\
                           %(id_name, self.getAbsLocalPath()))

        CTXRepository.__init__( self, id_name, local_path, href, None, version_control=False )
        self.msgSender = "CTXRepositoryFS"
示例#10
0
    def checkout( self, url, local_directory, revision ):
        self.resetClient()

        if self.isWorkingCopy( local_directory ):
            userErrorExit("Unable to checkout '%s', given target directory '%s' is an existing working copy."\
                           %(url, local_directory))

        if not self.isRepoURL(url):
            userErrorExit("Invalid SVN repository: '%s'"%(url))

        rev = self.prepareRevision(revision)

        if not os.path.isdir(local_directory):
            userErrorExit("Checkout destination '%s' is not a directory"%(local_directory))

        try:
            #pysvn checkout seems to have a problem when spaces are not escaped but also when other characters are escaped
            url = urllib.quote(url,  safe=',~=!@#$%^&*()+|}{:?><;[]\\/')
            self.client.checkout(url=url, path=local_directory, recurse=True, revision=rev)
        except pysvn.ClientError, e:
            for message, code in e.args[1]:
                if code in [ERR_HOST_UNRESOLVED,]:
                    errorMessage("Unable to resolve host '%s'. Proceeding.. "%(url))
                else:
                    userErrorExit("Unknown failure when checking out '%s'\nPYSVN Exception:\n%s (%d)"%(url, message, code))
示例#11
0
    def validateRepositories( self ):

        all_valid = True

        if not self.hasRSpec():
            return

        for repo in self.getRSpec().getRepositories():
            infoMessage("Validating repository '%s'"%(repo.getID()), 1)
            if not repo.checkValid( self.updating ):
                all_valid = False

        if all_valid == False:
            userErrorExit("Validation failed.")
示例#12
0
    def receive( self ):
        import pickle
        import sys
   
        data_buffer = str(sys.stdin.read())
    
        #
        # Locate package header
        #
        
        i = data_buffer.rfind( export_header )
        
        if i == -1:
            print data_buffer #Most likely errors from main system
            infoMessage("\n********** Export handler entry point **********\n\n", 2)
            userErrorExit("Unable to receive export package. Export header not found.\nThis is commonly the consequence of a terminal error raised by Contexo.")
            
        #
        # Extract package size
        #
        
        i += len(export_header)
        size_s = ""
        header_len = len(export_header)
        while data_buffer[i] != '$':
            size_s += data_buffer[i]
            header_len += 1
            i += 1
            
        header_len += 1
        package_start = i+1
        package_size  = int(size_s)
        package_end   = package_start + package_size
        
        #
        # Extract package chunk, and print everything else as regular text
        #
        
        package_dump = data_buffer[ package_start : package_end ]
        print data_buffer[ 0 : package_start - header_len ]
        print data_buffer[ package_end : -1 ]

        infoMessage("\n********** Export handler entry point **********\n\n", 2)
        
        #
        #  Unpickle
        #        
        
        self.export_data = pickle.loads( package_dump )
示例#13
0
    def receive( self ):
        import pickle
        import sys
   
        data_buffer = str(sys.stdin.read())
    
        #
        # Locate package header
        #
        
        i = data_buffer.rfind( export_header )
        
        if i == -1:
            print data_buffer #Most likely errors from main system
            infoMessage("\n********** Export handler entry point **********\n\n", 2)
            userErrorExit("Ctx export failed because of previous errors! Check the log for previous errors.")
            
        #
        # Extract package size
        #
        
        i += len(export_header)
        size_s = ""
        header_len = len(export_header)
        while data_buffer[i] != '$':
            size_s += data_buffer[i]
            header_len += 1
            i += 1
            
        header_len += 1
        package_start = i+1
        package_size  = int(size_s)
        package_end   = package_start + package_size
        
        #
        # Extract package chunk, and print >>sys.stderr, everything else as regular text
        #
        
        package_dump = data_buffer[ package_start : package_end ]
        print data_buffer[ 0 : package_start - header_len ]
        print data_buffer[ package_end : -1 ]

        infoMessage("\n********** Export handler entry point **********\n\n", 2)
        
        #
        #  Unpickle
        #        
        
        self.export_data = pickle.loads( package_dump )
示例#14
0
    def getLocalAccessPath(self):

        local_access_path = None

        if self.rcs == None:

            infoMessage("No RCS specified for RSpec '%s', attempting regular file access"\
                         %(self.getHref()), 4)

            if not os.path.exists(self.href):
                userErrorExit("RSpec unreachable with regular file access: \n  %s"%(self.href))

            local_access_path = self.getHref()

        else:

            temp_dir = getUserTempDir()
            rspec_name = os.path.basename(self.getHref())
            temp_rspec = os.path.join( temp_dir, rspec_name )

            infoMessage("Downloading (svn-exporting) RSpec from '%s' to '%s' using RCS '%s'"\
                         %(str(self.getHref()), str(temp_rspec), str(self.rcs)), 1)

            if os.path.exists(temp_rspec):
                os.remove( temp_rspec )

            # rspec access is still handled by svn
            if self.rcs == 'svn' or self.rcs == 'git':

                svn = ctx_svn_client.CTXSubversionClient()
                svn.export( self.getHref(), temp_dir, self.revision )
                local_access_path = temp_rspec

            #elif self.rcs == 'git':
                #git = ctx_git_client.CTXGitClient()

            else:
                userErrorExit("Unsupported RCS: %s"%(self.rcs))


        infoMessage("RSpec local access path resolved to: %s"\
                     %(local_access_path), 4)

        return local_access_path
示例#15
0
    def __init__(self, id_name, local_path, href, rev):
        self.path = os.path.abspath('')
        self.destpath = os.path.join(local_path, id_name)
        self.id_name = id_name
        self.git = locate_git()
        self.rev = rev

        if href == None:
            userErrorExit("No HREF specified for repository '%s'. Failed to aquire HREF from local copy '%s'"\
                           %(id_name, self.getAbsLocalPath()))

        CTXRepository.__init__( self, id_name, local_path, href, rev, version_control=True )
	
	# these are hardcoded dummy paths for ctxview
	self.local_path = local_path
	self.codeModulePaths = ['dummy1']
	self.componentPaths = ['dummy2']
	# END hardcoded dummy paths

	self.msgSender = "CTXRepositoryGIT"
示例#16
0
    def updateWorkingCopy( self, lc_path, rev ):
        self.resetClient()

        if not self.isWorkingCopy( lc_path ):
            userErrorExit("Unable to update '%s'. Not a valid working copy."\
                           %(lc_path))

        if rev.upper() == 'HEAD':
            rev = pysvn.Revision(pysvn.opt_revision_kind.head )
        else:
            rev = pysvn.Revision(pysvn.opt_revision_kind.number, rev )

        try:
            self.client.update( path=lc_path, recurse=True, revision=rev )

        except pysvn.ClientError, e:
            for message, code in e.args[1]:
                if code in [ERR_HOST_UNRESOLVED,]:
                    errorMessage("Unable to resolve host '%s'. Proceeding.. "\
                                  %(self.getURLFromWorkingCopy(lc_path)))
                else:
                    userErrorExit("Unknown failure when updating '%s'\nPYSVN Exception:\n%s (%d)"%(lc_path, message, code))
示例#17
0
    def __init__(self, view_path=str(), updating=False, validate=False ):
        self.localPath = os.path.abspath(view_path)
        if self.localPath.find(" ") > 0:
            userErrorExit("View dir name or any of it's subdirectories cannot contain space characters. View dir resolved to: \"" + self.localPath + "\"")
        self.global_paths = dict() # {section: pathlist}, where 'section' is any of SYSGLOBAL_PATH_SECTIONS
        self.rspec = None
        self.updating = updating # True when the view is being updated instead of used for building
        self.msgSender = "CTXView"

        infoMessage("Using view: %s "%(self.getRoot()), 2)

        for sec in SYSGLOBAL_PATH_SECTIONS:
            self.global_paths[sec] = list()

        self.process_view_directory()
        self.set_global_config()
        os.chdir( self.getRoot() )

        if validate:
            self.validateRepositories()
        else:
            infoMessage("Skipping repository validation", 2);
示例#18
0
    def locateItem(self, item, path_sections):

        candidate_locations = list()
        tried_locations = list()

        path_sections = assureList( path_sections )

        for path_section in path_sections:
            # Always prioritize locating items from the RSpec repositories
            if path_section in REPO_PATH_SECTIONS and self.getRSpec() != None:
                for repo in self.getRSpec().getRepositories():
                    repo_candidates, repo_tried = repo.locateItem( item, path_section )
                    candidate_locations.extend( repo_candidates )
                    tried_locations.extend( repo_tried )

            if len(candidate_locations) == 1:
                infoMessage("Located '%s' at '%s'"%(item, candidate_locations[0]), 2)
                return candidate_locations[0]
            elif len(candidate_locations) > 1:
                userErrorExit("Multiple occurances of '%s' was found. Unable to determine which one to use: \n   %s"\
                               %(item, "\n   ".join(candidate_locations)))

        # Item not present in any repository, be clear to the user..
        if self.getRSpec() != None:
            if self.getAccessPolicy() == AP_NO_REMOTE_ACCESS:
                infoMessage("Item '%s' was not found in RSpec repositories.\nNote that the system is set to search for repository items in the local view only (%s).\nTrying system global locations."%(item, AP_FLAGS[self.getAccessPolicy()]), 2)
            elif self.getAccessPolicy() == AP_PREFER_REMOTE_ACCESS:
                infoMessage("Item '%s' was not found in RSpec repositories.\nNote that the system is set to search for repository items at the repository source location only (%s).\nTrying system global locations."%(item, AP_FLAGS[self.getAccessPolicy()]), 2)
            else:
                infoMessage("Item '%s' was not found in RSpec repositories.\nTrying system global locations."%(item), 2)

        for path_section in path_sections:
            # File was not found in RSpec repositories, look in view
            if path_section in SYSGLOBAL_PATH_SECTIONS:
                for path in self.getGlobalPaths(path_section):
                    tried_locations.append( path )
                    if os.path.isdir(path):
                        if item in os.listdir(path):
                            candidate_locations.append( os.path.join(path, item) )
                    else:
                        tried_locations[-1] = tried_locations[-1] + " (path not found)"
                        warningMessage("System global path '%s' doesn't exist"%(path))

            if len(candidate_locations) == 1:
                infoMessage("Located '%s' at '%s'"%(item, candidate_locations[0]), 2)
                return candidate_locations[0]
            elif len(candidate_locations) > 1:
                userErrorExit("Multiple occurances of '%s' was found. Unable to determine which one to use: \n   %s"\
                               %(item, "\n   ".join(candidate_locations)))


        userErrorExit("Unable to locate file '%s'. Attempted the following locations: \n   %s"\
                       %(item, "\n   ".join(tried_locations)))
示例#19
0
    def getLocalAccessPath(self):
        # TODO: wipe cache if root element and first export ok
        src = str()

        local_access_path = os.path.join( self.rspec_cache_dir, os.path.basename(self.getHref()))
        if not os.path.exists(self.rspec_cache_dir):
            os.makedirs(self.rspec_cache_dir)
 
        if self.rcs == None:
            if not os.path.exists( local_access_path ):
                infoMessage("No RCS specified for RSpec '%s', attempting regular file access"\
                         %(self.getHref()), 4)
                if not os.path.exists(self.href):
                    userErrorExit("RSpec unreachable with regular file access: \n  %s"%(self.href))
                shutil.copyfile( self.getHref(), local_access_path )
            # access the local rspec if accessable
            # otherwise the user may be confused why changes in a locally modified would not be applied.
            if os.path.exists(self.href):
                local_access_path = self.getHref()

        else:
            if self.updating == True or not os.path.exists( local_access_path ):
                temp_dir = getUserTempDir()
                rspec_name = os.path.basename(self.getHref())
                temp_rspec = os.path.join( temp_dir, rspec_name )

                infoMessage("Downloading (svn-exporting) RSpec from '%s' to '%s' using RCS '%s'"\
                         %(str(self.getHref()), str(temp_rspec), str(self.rcs)), 1)

                if os.path.exists(temp_rspec):
                    os.remove( temp_rspec )

                # rspec access is still handled by svn
                if self.rcs == 'svn' or self.rcs == 'git':

                    svn = ctx_svn_client.CTXSubversionClient()
                    # does this fail if rspec cannot be fetched?
                    svn.export( self.getHref(), temp_dir, self.revision )
                    if not os.path.exists( os.path.join(temp_dir, os.path.basename(self.getHref()))):
                        userErrorExit("RSpec unreachable with remote Subversion access: \n  %s"%(self.getHref()))

                else:
                    userErrorExit("Unsupported RCS: %s"%(self.rcs))
                src = os.path.join( temp_dir, rspec_name)
                shutil.copyfile( src, local_access_path )

        infoMessage("RSpec local access path resolved to: %s"\
                     %(local_access_path), 4)

        return local_access_path
示例#20
0
    def locateItem(self, item, path_sections):

        candidate_locations = list()
        tried_locations = list()

        path_sections = assureList( path_sections )

        for path_section in path_sections:
            # Always prioritize locating items from the RSpec repositories
            if path_section in REPO_PATH_SECTIONS and self.getRSpec() != None:
                for repo in self.getRSpec().getRepositories():
                    repo_candidates, repo_tried = repo.locateItem( item, path_section )
                    candidate_locations.extend( repo_candidates )
                    tried_locations.extend( repo_tried )

            if len(candidate_locations) == 1:
                infoMessage("Located '%s' at '%s'"%(item, candidate_locations[0]), 2)
                return candidate_locations[0]
            elif len(candidate_locations) > 1:
                userErrorExit("Multiple occurances of '%s' was found. Unable to determine which one to use: \n   %s"\
                               %(item, "\n   ".join(candidate_locations)))
            infoMessage("Item '%s' was not found in RSpec repositories.\nTrying system global locations."%(item), 2) 

        for path_section in path_sections:
            # File was not found in RSpec repositories, look in view
            if path_section in SYSGLOBAL_PATH_SECTIONS:
                for path in self.getGlobalPaths(path_section):
                    tried_locations.append( path )
                    if os.path.isdir(path):
                        if item in os.listdir(path):
                            candidate_locations.append( os.path.join(path, item) )
                    else:
                        tried_locations[-1] = tried_locations[-1] + " (path not found)"
                        warningMessage("System global path '%s' doesn't exist"%(path))

            if len(candidate_locations) == 1:
                infoMessage("Located '%s' at '%s'"%(item, candidate_locations[0]), 2)
                return candidate_locations[0]
            elif len(candidate_locations) > 1:
                userErrorExit("Multiple occurances of '%s' was found. Unable to determine which one to use: \n   %s"\
                               %(item, "\n   ".join(candidate_locations)))


        userErrorExit("Unable to locate file '%s'. Attempted the following locations: \n   %s"\
                       %(item, "\n   ".join(tried_locations)))
示例#21
0
    def export( self, url, local_directory, revision ):
        self.resetClient()

        if not self.isRepoURL(url):
            userErrorExit("Invalid SVN repository: '%s'"%(url))

        rev = self.prepareRevision(revision)

        if not os.path.isdir( local_directory ):
            os.makedirs( local_directory )
        elif self.isWorkingCopy( local_directory ):
            userErrorExit("Export destination '%s' is an existing working copy"%(local_directory))

        export_dest = os.path.join( local_directory, os.path.basename(url) )

        try:
            url = urllib.quote(url,  safe=',~=!@#$%^&*()+|}{:?><;[]\\/')
            self.client.export(src_url_or_path=url, dest_path=export_dest, force=False, revision=rev)
        except :
            userErrorExit("Exception caught from pysvn: \n%s"%(sys.exc_value))
示例#22
0
def locate_git():
    for git_cand in ['git', 'git.cmd', 'git.exe', 'git.bat']:
        for path in os.environ["PATH"].split(os.pathsep):
            if os.path.exists(os.path.join( path, git_cand)) and os.access( os.path.join( path, git_cand), os.X_OK):
                return git_cand
    userErrorExit("Git cannot be found in your PATH. Please re-install Git and make sure the git.cmd, git.exe or git binary can be found in your PATH")
示例#23
0
    def startElement(self, name, attrs):

        # .....................................................................
        if name == 'ctx-rspec':
            if self.parent_element.peek() != None:
                userErrorExit("'<ctx-rspec>' can only be used as root element")

        # .....................................................................
        elif name == 'ctx-import':
            # Make sure this element is used in correct context
            if self.parent_element.peek() != 'ctx-rspec':
                userErrorExit("'%s' elements can only be used within '%s' elements"%(name, 'ctx-rspec'))

            #
            # Digest import and check for errors/inconsistencies
            #

            href = attrs.get('href', None)
            if href == None:
                userErrorExit("<ctx-import>: Missing mandatory attribute '%s' in RSpec '%s'"%('href', self.rspecFile.getFilename()))

            rcs = attrs.get('rcs', None)
            if rcs == None and attrs.has_key('rev'):
                userErrorExit("<ctx-import>: Revision ('rev') specified without specifying 'rcs' in RSpec '%s'"%(self.rspecFile.getFilename()))

            rev = attrs.get('rev', None )
            if rcs != None and rev == None:
                warningMessage("<ctx-import>: No revision specified for import in RSpec '%s'. Assuming 'HEAD'"%(self.rspecFile.getFilename()))
                rev = 'HEAD'

            # Prepare locator object and add import to current RSpec
            rspecFileLocator = RSpecFileLocator( rcs=rcs, href=href, revision=rev, updating = False, wipe_cache=False, view=self.view )
            self.rspecFile.addImport( rspecFileLocator ) # POINT OF RECURSION

        # .....................................................................
        elif name == 'ctx-repo':

            # Make sure this element is used in correct context
            if self.parent_element.peek() != 'ctx-rspec':
                userErrorExit("'%s' elements can only be used within '%s' elements"%(name, 'ctx-rspec'))

            # Make sure 'id' attribute is unique within RSpec
            if attrs.has_key('id'):
                if attrs.get('id') in self.id_list:
                    userErrorExit("Multiple occurances of id '%s' in '%s'"%(attrs.get('id'), self.rspecFile.getFilename()))
                else:
                    self.id_list.append(attrs.get('id'))

            self.current_repo = createRepoFromRCS( attrs.get('rcs', None),
                                                   attrs.get('id', ""),
                                                   attrs.get('path', ""),
                                                   attrs.get('href', ""),
                                                   attrs.get('rev', ""))
            self.rspecFile.addRepository ( self.current_repo )

        # .....................................................................
        elif name == 'ctx-path':

           # Make sure this element is used in correct context
            if self.parent_element.peek() not in ['ctx-repo',]:
                userErrorExit("'<%s>' elements cannot be used within '<%s>' elements"%(name, self.parent_element.peek()))

            #
            # Assure presence of mandatory attributes
            #

            attribute = 'type'
            if not attrs.has_key(attribute):
                userErrorExit("Missing mandatory attribute '%s' in element '<%s>'"%(attribute, name))

            attribute = 'spec'
            if not attrs.has_key(attribute):
                userErrorExit("Missing mandatory attribute '%s' in element '<%s>'"%(attribute, name))

            ctx_path_type = attrs.get( 'type' ).lower()
            self.current_repo.addPath( ctx_path_type, attrs.get('spec').replace('\\','/') )

        # .....................................................................
        else:
            warningMessage("Ignoring unknown element '<%s>' in RSpec '%s'.\nThis might be a normal compatibility issue."%(name, self.rspecFile.getFilename()))

        self.parent_element.push( name )