hgext/clang-format/__init__.py (75 lines of code) (raw):

# This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. """ run clang-format as part of the commit For this, we override the commit command, run: ./mach clang-format -p <file> and call the mercurial commit function """ import os import shutil import subprocess from mercurial import ( cmdutil, error, extensions, localrepo, match, pycompat, scmutil, ) OUR_DIR = os.path.dirname(__file__) with open(os.path.join(OUR_DIR, "..", "bootstrap.py")) as f: exec(f.read()) from mozhg.util import is_firefox_repo testedwith = b"4.4 4.5 4.6 4.7 4.8 4.9 5.0 5.1 5.2 5.3" minimumhgversion = b"4.4" buglink = b"https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox%20Build%20System&component=Lint%20and%20Formatting" # noqa: E501 def find_python(): for python_variant in ("py", "python3", "python"): if shutil.which(python_variant): return python_variant.encode("utf-8") raise error.Abort(b"Could not find a suitable Python to run `mach`!") def call_clang_format(repo, changed_files): """Call `./mach clang-format` on the changed files""" # We have also a copy of this list in: # python/mozbuild/mozbuild/mach_commands.py # tools/lint/hooks_clang_format.py # release-services/src/staticanalysis/bot/static_analysis_bot/config.py # Too heavy to import the full class just for this variable extensions = (b".cpp", b".c", b".cc", b".h", b".m", b".mm") path_list = [] for filename in sorted(changed_files): # Ignore files unsupported in clang-format if filename.endswith(extensions): path_list.append(filename) if not path_list: # No files have been touched return clang_format_cmd = [ find_python(), os.path.join(repo.root, b"mach"), b"clang-format", b"-p", ] + path_list # Set `PYTHONIOENCODING` since `hg.exe` will detect `cp1252` as the encoding # and pass it as the encoding to `mach` via the environment. env = dict(os.environ) env["PYTHONIOENCODING"] = "utf-8" subprocess.call(clang_format_cmd, env=env) def wrappedcommit(orig, repo, *args, **kwargs): try: path_matcher = args[3] except IndexError: # In a rebase for example return orig(repo, *args, **kwargs) # In case hg changes the position of the arg # path_matcher will be empty in case of histedit assert isinstance(path_matcher, match.basematcher) or path_matcher is None try: lock = repo.wlock() status = repo.status(match=path_matcher) changed_files = sorted(status.modified + status.added) if changed_files: call_clang_format(repo, changed_files) except Exception as e: repo.ui.warn(b"Exception %s\n" % pycompat.bytestr(str(e))) finally: lock.release() return orig(repo, *args, **kwargs) def wrappedamend(orig, ui, repo, old, extra, pats, opts): """Wraps cmdutil.amend to run clang-format during `hg commit --amend`""" wctx = repo[None] matcher = scmutil.match(wctx, pats, opts) filestoamend = [f for f in wctx.files() if matcher(f)] if not filestoamend: return orig(ui, repo, old, extra, pats, opts) try: with repo.wlock(): call_clang_format(repo, filestoamend) except Exception as e: repo.ui.warn(b"Exception %s\n" % pycompat.bytestr(str(e))) return orig(ui, repo, old, extra, pats, opts) def reposetup(ui, repo): # Avoid setup altogether if `moz-phab` is executing hg, # or the repository is not a Firefox derivative, # or the repo is not local if not repo.local() or "MOZPHAB" in os.environ or not is_firefox_repo(repo): return extensions.wrapfunction(localrepo.localrepository, "commit", wrappedcommit) extensions.wrapfunction(cmdutil, "amend", wrappedamend)