113540Sandrea.mondelli@ucf.edu#!/usr/bin/env python2.7 211406Sandreas.sandberg@arm.com# 311406Sandreas.sandberg@arm.com# Copyright (c) 2016 ARM Limited 411406Sandreas.sandberg@arm.com# All rights reserved 511406Sandreas.sandberg@arm.com# 611406Sandreas.sandberg@arm.com# The license below extends only to copyright in the software and shall 711406Sandreas.sandberg@arm.com# not be construed as granting a license to any other intellectual 811406Sandreas.sandberg@arm.com# property including but not limited to intellectual property relating 911406Sandreas.sandberg@arm.com# to a hardware implementation of the functionality of the software 1011406Sandreas.sandberg@arm.com# licensed hereunder. You may use the software subject to the license 1111406Sandreas.sandberg@arm.com# terms below provided that you ensure that this notice is replicated 1211406Sandreas.sandberg@arm.com# unmodified and in its entirety in all distributions of the software, 1311406Sandreas.sandberg@arm.com# modified or unmodified, in source code or in binary form. 1411406Sandreas.sandberg@arm.com# 1511406Sandreas.sandberg@arm.com# Redistribution and use in source and binary forms, with or without 1611406Sandreas.sandberg@arm.com# modification, are permitted provided that the following conditions are 1711406Sandreas.sandberg@arm.com# met: redistributions of source code must retain the above copyright 1811406Sandreas.sandberg@arm.com# notice, this list of conditions and the following disclaimer; 1911406Sandreas.sandberg@arm.com# redistributions in binary form must reproduce the above copyright 2011406Sandreas.sandberg@arm.com# notice, this list of conditions and the following disclaimer in the 2111406Sandreas.sandberg@arm.com# documentation and/or other materials provided with the distribution; 2211406Sandreas.sandberg@arm.com# neither the name of the copyright holders nor the names of its 2311406Sandreas.sandberg@arm.com# contributors may be used to endorse or promote products derived from 2411406Sandreas.sandberg@arm.com# this software without specific prior written permission. 2511406Sandreas.sandberg@arm.com# 2611406Sandreas.sandberg@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2711406Sandreas.sandberg@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2811406Sandreas.sandberg@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2911406Sandreas.sandberg@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3011406Sandreas.sandberg@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3111406Sandreas.sandberg@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3211406Sandreas.sandberg@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3311406Sandreas.sandberg@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3411406Sandreas.sandberg@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3511406Sandreas.sandberg@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3611406Sandreas.sandberg@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3711406Sandreas.sandberg@arm.com# 3811406Sandreas.sandberg@arm.com# Authors: Andreas Sandberg 3911406Sandreas.sandberg@arm.com 4011406Sandreas.sandberg@arm.comfrom abc import * 4111406Sandreas.sandberg@arm.comimport os 4211406Sandreas.sandberg@arm.comimport subprocess 4311406Sandreas.sandberg@arm.com 4411406Sandreas.sandberg@arm.comfrom region import * 4511406Sandreas.sandberg@arm.comfrom style import modified_regions 4611406Sandreas.sandberg@arm.com 4711406Sandreas.sandberg@arm.comclass AbstractRepo(object): 4811406Sandreas.sandberg@arm.com __metaclass__ = ABCMeta 4911406Sandreas.sandberg@arm.com 5011406Sandreas.sandberg@arm.com def file_path(self, fname): 5111406Sandreas.sandberg@arm.com """Get the absolute path to a file relative within the repository. The 5211406Sandreas.sandberg@arm.com input file name must be a valid path within the repository. 5311406Sandreas.sandberg@arm.com 5411406Sandreas.sandberg@arm.com """ 5511406Sandreas.sandberg@arm.com return os.path.join(self.repo_base(), fname) 5611406Sandreas.sandberg@arm.com 5711406Sandreas.sandberg@arm.com def in_repo(self, fname): 5811406Sandreas.sandberg@arm.com """Check if a path points to something within the repository base. Not 5911406Sandreas.sandberg@arm.com that this does not check for the presence of the object in the 6011406Sandreas.sandberg@arm.com file system as it could exist in the index without being in 6111406Sandreas.sandberg@arm.com the file system. 6211406Sandreas.sandberg@arm.com 6311406Sandreas.sandberg@arm.com """ 6411406Sandreas.sandberg@arm.com fname = os.path.abspath(fname) 6511406Sandreas.sandberg@arm.com repo_path = os.path.abspath(self.repo_base()) 6611406Sandreas.sandberg@arm.com 6711406Sandreas.sandberg@arm.com return os.path.commonprefix([repo_path, fname]) == repo_path 6811406Sandreas.sandberg@arm.com 6911406Sandreas.sandberg@arm.com def repo_path(self, fname): 7011406Sandreas.sandberg@arm.com """Get the path of a file relative to the repository base. The input 7111406Sandreas.sandberg@arm.com file name is assumed to be an absolute path or a path relative 7211406Sandreas.sandberg@arm.com to the current working directory. 7311406Sandreas.sandberg@arm.com 7411406Sandreas.sandberg@arm.com """ 7511406Sandreas.sandberg@arm.com return os.path.relpath(fname, self.repo_base()) 7611406Sandreas.sandberg@arm.com 7711406Sandreas.sandberg@arm.com def get_file(self, name): 7811406Sandreas.sandberg@arm.com """Get the contents of a file in the file system using a path relative 7911406Sandreas.sandberg@arm.com to the repository root. 8011406Sandreas.sandberg@arm.com 8111406Sandreas.sandberg@arm.com """ 8211406Sandreas.sandberg@arm.com with open(self.file_path(name), "r") as f: 8311406Sandreas.sandberg@arm.com return f.read() 8411406Sandreas.sandberg@arm.com 8511406Sandreas.sandberg@arm.com @abstractmethod 8611406Sandreas.sandberg@arm.com def repo_base(self): 8711406Sandreas.sandberg@arm.com """Get the path to the base of the repository""" 8811406Sandreas.sandberg@arm.com pass 8911406Sandreas.sandberg@arm.com 9011406Sandreas.sandberg@arm.com @abstractmethod 9111406Sandreas.sandberg@arm.com def staged_files(self): 9211406Sandreas.sandberg@arm.com """Get a tuple describing the files that have been staged for a 9311406Sandreas.sandberg@arm.com commit: (list of new, list of modified) 9411406Sandreas.sandberg@arm.com 9511406Sandreas.sandberg@arm.com """ 9611406Sandreas.sandberg@arm.com pass 9711406Sandreas.sandberg@arm.com 9811406Sandreas.sandberg@arm.com @abstractmethod 9911406Sandreas.sandberg@arm.com def staged_regions(self, fname, context=0): 10011406Sandreas.sandberg@arm.com """Get modified regions that will be committed by the next commit 10111406Sandreas.sandberg@arm.com command 10211406Sandreas.sandberg@arm.com 10311406Sandreas.sandberg@arm.com """ 10411406Sandreas.sandberg@arm.com pass 10511406Sandreas.sandberg@arm.com 10611406Sandreas.sandberg@arm.com @abstractmethod 10711406Sandreas.sandberg@arm.com def modified_regions(self, fname, context=0): 10811406Sandreas.sandberg@arm.com """Get modified regions that have been staged for commit or are 10911406Sandreas.sandberg@arm.com present in the file system. 11011406Sandreas.sandberg@arm.com 11111406Sandreas.sandberg@arm.com """ 11211406Sandreas.sandberg@arm.com pass 11311406Sandreas.sandberg@arm.com 11411406Sandreas.sandberg@arm.comclass GitRepo(AbstractRepo): 11511406Sandreas.sandberg@arm.com def __init__(self): 11611406Sandreas.sandberg@arm.com self.git = "git" 11711406Sandreas.sandberg@arm.com self._head_revision = None 11811406Sandreas.sandberg@arm.com self._repo_base = None 11911406Sandreas.sandberg@arm.com 12011406Sandreas.sandberg@arm.com def repo_base(self): 12111406Sandreas.sandberg@arm.com if self._repo_base is None: 12211406Sandreas.sandberg@arm.com self._repo_base = subprocess.check_output( 12311406Sandreas.sandberg@arm.com [ self.git, "rev-parse", "--show-toplevel" ]).rstrip("\n") 12411406Sandreas.sandberg@arm.com 12511406Sandreas.sandberg@arm.com return self._repo_base 12611406Sandreas.sandberg@arm.com 12711406Sandreas.sandberg@arm.com def staged_files(self): 12811406Sandreas.sandberg@arm.com added = [] 12911406Sandreas.sandberg@arm.com modified = [] 13011406Sandreas.sandberg@arm.com for action, fname in self.status(filter="MA", cached=True): 13111406Sandreas.sandberg@arm.com if action == "M": 13211406Sandreas.sandberg@arm.com modified.append(fname) 13311406Sandreas.sandberg@arm.com elif action == "A": 13411406Sandreas.sandberg@arm.com added.append(fname) 13511406Sandreas.sandberg@arm.com 13611406Sandreas.sandberg@arm.com return added, modified 13711406Sandreas.sandberg@arm.com 13811406Sandreas.sandberg@arm.com def staged_regions(self, fname, context=0): 13911406Sandreas.sandberg@arm.com if self.file_status(fname, cached=True) in ("", "A", ): 14011406Sandreas.sandberg@arm.com return all_regions 14111406Sandreas.sandberg@arm.com 14211406Sandreas.sandberg@arm.com old = self.file_from_head(self.repo_path(fname)).split("\n") 14311406Sandreas.sandberg@arm.com new = self.file_from_index(self.repo_path(fname)).split("\n") 14411406Sandreas.sandberg@arm.com 14511406Sandreas.sandberg@arm.com return modified_regions(old, new, context=context) 14611406Sandreas.sandberg@arm.com 14711406Sandreas.sandberg@arm.com def modified_regions(self, fname, context=0): 14811406Sandreas.sandberg@arm.com if self.file_status(fname) in ("", "A", ): 14911406Sandreas.sandberg@arm.com return all_regions 15011406Sandreas.sandberg@arm.com 15111406Sandreas.sandberg@arm.com old = self.file_from_head(self.repo_path(fname)).split("\n") 15211406Sandreas.sandberg@arm.com new = self.get_file(self.repo_path(fname)).split("\n") 15311406Sandreas.sandberg@arm.com 15411406Sandreas.sandberg@arm.com return modified_regions(old, new, context=context) 15511406Sandreas.sandberg@arm.com 15611406Sandreas.sandberg@arm.com 15711406Sandreas.sandberg@arm.com def head_revision(self): 15811406Sandreas.sandberg@arm.com if self._head_revision is not None: 15911406Sandreas.sandberg@arm.com return self._head_revision 16011406Sandreas.sandberg@arm.com 16111406Sandreas.sandberg@arm.com try: 16211406Sandreas.sandberg@arm.com self._head_revision = subprocess.check_output( 16311406Sandreas.sandberg@arm.com [ self.git, "rev-parse", "--verify", "HEAD" ], 16411406Sandreas.sandberg@arm.com stderr=subprocess.PIPE).rstrip("\n") 16511406Sandreas.sandberg@arm.com except subprocess.CalledProcessError: 16611406Sandreas.sandberg@arm.com # Assume that the repo is empty and use the semi-magic 16711406Sandreas.sandberg@arm.com # empty tree revision if git rev-parse returned an error. 16811406Sandreas.sandberg@arm.com self._head_revision = "4b825dc642cb6eb9a060e54bf8d69288fbee4904" 16911406Sandreas.sandberg@arm.com 17011406Sandreas.sandberg@arm.com return self._head_revision 17111406Sandreas.sandberg@arm.com 17211406Sandreas.sandberg@arm.com def file_status(self, fname, cached=False): 17311406Sandreas.sandberg@arm.com status = self.status(files=[fname], cached=cached) 17411406Sandreas.sandberg@arm.com assert len(status) <= 1 17511406Sandreas.sandberg@arm.com if status: 17611406Sandreas.sandberg@arm.com return status[0][0] 17711406Sandreas.sandberg@arm.com else: 17811406Sandreas.sandberg@arm.com # No information available for the file. This usually 17911406Sandreas.sandberg@arm.com # means that it hasn't been added to the 18011406Sandreas.sandberg@arm.com # repository/commit. 18111406Sandreas.sandberg@arm.com return "" 18211406Sandreas.sandberg@arm.com 18311406Sandreas.sandberg@arm.com def status(self, filter=None, files=[], cached=False): 18411406Sandreas.sandberg@arm.com cmd = [ self.git, "diff-index", "--name-status" ] 18511406Sandreas.sandberg@arm.com if cached: 18611406Sandreas.sandberg@arm.com cmd.append("--cached") 18711406Sandreas.sandberg@arm.com if filter: 18811466SCurtis.Dunham@arm.com cmd += [ "--diff-filter=%s" % filter ] 18911406Sandreas.sandberg@arm.com cmd += [ self.head_revision(), "--" ] + files 19011406Sandreas.sandberg@arm.com status = subprocess.check_output(cmd).rstrip("\n") 19111406Sandreas.sandberg@arm.com 19211406Sandreas.sandberg@arm.com if status: 19311406Sandreas.sandberg@arm.com return [ f.split("\t") for f in status.split("\n") ] 19411406Sandreas.sandberg@arm.com else: 19511406Sandreas.sandberg@arm.com return [] 19611406Sandreas.sandberg@arm.com 19711406Sandreas.sandberg@arm.com def file_from_index(self, name): 19811406Sandreas.sandberg@arm.com return subprocess.check_output( 19911406Sandreas.sandberg@arm.com [ self.git, "show", ":%s" % (name, ) ]) 20011406Sandreas.sandberg@arm.com 20111406Sandreas.sandberg@arm.com def file_from_head(self, name): 20211406Sandreas.sandberg@arm.com return subprocess.check_output( 20311406Sandreas.sandberg@arm.com [ self.git, "show", "%s:%s" % (self.head_revision(), name) ]) 20411406Sandreas.sandberg@arm.com 20511406Sandreas.sandberg@arm.comclass MercurialRepo(AbstractRepo): 20611406Sandreas.sandberg@arm.com def __init__(self): 20711406Sandreas.sandberg@arm.com self.hg = "hg" 20811406Sandreas.sandberg@arm.com self._repo_base = None 20911406Sandreas.sandberg@arm.com 21011406Sandreas.sandberg@arm.com def repo_base(self): 21111406Sandreas.sandberg@arm.com if self._repo_base is None: 21211406Sandreas.sandberg@arm.com self._repo_base = subprocess.check_output( 21311406Sandreas.sandberg@arm.com [ self.hg, "root" ]).rstrip("\n") 21411406Sandreas.sandberg@arm.com 21511406Sandreas.sandberg@arm.com return self._repo_base 21611406Sandreas.sandberg@arm.com 21711406Sandreas.sandberg@arm.com def staged_files(self): 21811406Sandreas.sandberg@arm.com added = [] 21911406Sandreas.sandberg@arm.com modified = [] 22011406Sandreas.sandberg@arm.com for action, fname in self.status(): 22111406Sandreas.sandberg@arm.com if action == "M": 22211406Sandreas.sandberg@arm.com modified.append(fname) 22311406Sandreas.sandberg@arm.com elif action == "A": 22411406Sandreas.sandberg@arm.com added.append(fname) 22511406Sandreas.sandberg@arm.com 22611406Sandreas.sandberg@arm.com return added, modified 22711406Sandreas.sandberg@arm.com 22811406Sandreas.sandberg@arm.com def staged_regions(self, fname, context=0): 22911406Sandreas.sandberg@arm.com return self.modified_regions(fname, context=context) 23011406Sandreas.sandberg@arm.com 23111406Sandreas.sandberg@arm.com def modified_regions(self, fname, context=0): 23211406Sandreas.sandberg@arm.com old = self.file_from_tip(fname).split("\n") 23311406Sandreas.sandberg@arm.com new = self.get_file(fname).split("\n") 23411406Sandreas.sandberg@arm.com 23511406Sandreas.sandberg@arm.com return modified_regions(old, new, context=context) 23611406Sandreas.sandberg@arm.com 23711406Sandreas.sandberg@arm.com def status(self, filter=None): 23811406Sandreas.sandberg@arm.com files = subprocess.check_output([ self.hg, "status" ]).rstrip("\n") 23911406Sandreas.sandberg@arm.com if files: 24011406Sandreas.sandberg@arm.com return [ f.split(" ") for f in files.split("\n") ] 24111406Sandreas.sandberg@arm.com else: 24211406Sandreas.sandberg@arm.com return [] 24311406Sandreas.sandberg@arm.com 24411406Sandreas.sandberg@arm.com def file_from_tip(self, name): 24511406Sandreas.sandberg@arm.com return subprocess.check_output([ self.hg, "cat", name ]) 24611406Sandreas.sandberg@arm.com 24711406Sandreas.sandberg@arm.comdef detect_repo(path="."): 24811406Sandreas.sandberg@arm.com """Auto-detect the revision control system used for a source code 24911406Sandreas.sandberg@arm.com directory. The code starts searching for repository meta data 25011406Sandreas.sandberg@arm.com directories in path and then continues towards the root directory 25111406Sandreas.sandberg@arm.com until root is reached or a metadatadirectory has been found. 25211406Sandreas.sandberg@arm.com 25311406Sandreas.sandberg@arm.com Returns: List of repository helper classes that can interface with 25411406Sandreas.sandberg@arm.com the detected revision control system(s). 25511406Sandreas.sandberg@arm.com 25611406Sandreas.sandberg@arm.com """ 25711406Sandreas.sandberg@arm.com 25811406Sandreas.sandberg@arm.com _repo_types = ( 25911406Sandreas.sandberg@arm.com (".git", GitRepo), 26011406Sandreas.sandberg@arm.com (".hg", MercurialRepo), 26111406Sandreas.sandberg@arm.com ) 26211406Sandreas.sandberg@arm.com 26311406Sandreas.sandberg@arm.com repo_types = [] 26411406Sandreas.sandberg@arm.com for repo_dir, repo_class in _repo_types: 26511406Sandreas.sandberg@arm.com if os.path.exists(os.path.join(path, repo_dir)): 26611406Sandreas.sandberg@arm.com repo_types.append(repo_class) 26711406Sandreas.sandberg@arm.com 26811406Sandreas.sandberg@arm.com if repo_types: 26911406Sandreas.sandberg@arm.com return repo_types 27011406Sandreas.sandberg@arm.com else: 27111406Sandreas.sandberg@arm.com parent_dir = os.path.abspath(os.path.join(path, "..")) 27211406Sandreas.sandberg@arm.com if not os.path.samefile(parent_dir, path): 27311406Sandreas.sandberg@arm.com return detect_repo(path=parent_dir) 27411406Sandreas.sandberg@arm.com else: 27511406Sandreas.sandberg@arm.com # We reached the root directory without finding a meta 27611406Sandreas.sandberg@arm.com # data directory. 27711406Sandreas.sandberg@arm.com return [] 278