build: move libcc patches to electron repo (#14104)

In the GN build, libchromiumcontent is no longer a distinct library, but
merely a container for a set of scripts and patches. Maintaining those
patches in a separate repository is tedious and error-prone, so merge
them into the main repo.

Once this is merged and GN is the default way to build Electron, the
libchromiumcontent repository can be archived.
This commit is contained in:
Jeremy Apthorp 2018-09-13 22:02:16 -07:00 committed by GitHub
parent 9e85bdb02c
commit 76c5f5cc8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
147 changed files with 86931 additions and 6 deletions

105
script/lib/git.py Normal file
View file

@ -0,0 +1,105 @@
"""Git helper functions.
Everything in here should be project agnostic, shouldn't rely on project's structure,
and make any assumptions about the passed arguments or calls outcomes.
"""
import os
import subprocess
from util import scoped_cwd
def is_repo_root(path):
path_exists = os.path.exists(path)
if not path_exists:
return False
git_folder_path = os.path.join(path, '.git')
git_folder_exists = os.path.exists(git_folder_path)
return git_folder_exists
def get_repo_root(path):
"""Finds a closest ancestor folder which is a repo root."""
norm_path = os.path.normpath(path)
norm_path_exists = os.path.exists(norm_path)
if not norm_path_exists:
return None
if is_repo_root(norm_path):
return norm_path
parent_path = os.path.dirname(norm_path)
# Check if we're in the root folder already.
if parent_path == norm_path:
return None
return get_repo_root(parent_path)
def apply(repo, patch_path, directory=None, index=False, reverse=False):
args = ['git', 'apply',
'--ignore-space-change',
'--ignore-whitespace',
'--whitespace', 'fix'
]
if directory:
args += ['--directory', directory]
if index:
args += ['--index']
if reverse:
args += ['--reverse']
args += ['--', patch_path]
with scoped_cwd(repo):
return_code = subprocess.call(args)
applied_successfully = (return_code == 0)
return applied_successfully
def get_patch(repo, commit_hash):
args = ['git', 'diff-tree',
'-p',
commit_hash,
'--' # Explicitly tell Git that `commit_hash` is a revision, not a path.
]
with scoped_cwd(repo):
return subprocess.check_output(args)
def get_head_commit(repo):
args = ['git', 'rev-parse', 'HEAD']
with scoped_cwd(repo):
return subprocess.check_output(args).strip()
def reset(repo):
args = ['git', 'reset']
with scoped_cwd(repo):
subprocess.check_call(args)
def commit(repo, author, message):
""" Commit whatever in the index is now."""
# Let's setup committer info so git won't complain about it being missing.
# TODO: Is there a better way to set committer's name and email?
env = os.environ.copy()
env['GIT_COMMITTER_NAME'] = 'Anonymous Committer'
env['GIT_COMMITTER_EMAIL'] = 'anonymous@electronjs.org'
args = ['git', 'commit',
'--author', author,
'--message', message
]
with scoped_cwd(repo):
return_code = subprocess.call(args, env=env)
committed_successfully = (return_code == 0)
return committed_successfully

168
script/lib/patches.py Normal file
View file

@ -0,0 +1,168 @@
import os
import sys
import git
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
VENDOR_DIR = os.path.join(SOURCE_ROOT, 'vendor')
PYYAML_LIB_DIR = os.path.join(VENDOR_DIR, 'pyyaml', 'lib')
sys.path.append(PYYAML_LIB_DIR)
import yaml
class Patch:
def __init__(self, file_path, repo_path, paths_prefix=None, author='Anonymous <anonymous@electronjs.org>', description=None):
self.author = author
self.description = description
self.file_path = file_path
self.paths_prefix = paths_prefix
self.repo_path = repo_path
def apply(self, reverse=False, commit=False, index=False):
# Add the change to index only if we're going to commit it later.
add_to_index = index or commit
patch_applied = git.apply(self.repo_path, self.file_path, directory=self.paths_prefix, index=add_to_index, reverse=reverse)
if not patch_applied:
return False
if commit:
message = self.__get_commit_message(reverse)
patch_committed = git.commit(self.repo_path, author=self.author, message=message)
return patch_committed
return True
def __get_commit_message(self, reverse):
message = self.description
if message is None:
message = os.path.basename(self.file_path)
if reverse:
message = 'Revert: ' + message
return message
def reverse(self):
return self.apply(reverse=True)
def get_file_path(self):
return self.file_path
class PatchesList:
"""A list of patches for a specific git repo."""
def __init__(self, repo_path, patches):
# TODO(alexeykuzmin): Make sure that all patches have the same repo_path.
self.repo_path = repo_path
self.patches = patches
def __len__(self):
return len(self.patches)
def apply(self, reverse=False, stop_on_error=True, commit=False):
all_patches_applied = True
failed_patches = []
for patch in self.patches:
# Even if `commit` is True we're not going to commit
# individual patches, it takes too much time in the Chromium repo.
# Applying all commits takes about 10 minutes (!) on a fast dev machine.
# Instead of it we are going only to add all changes to the index
# and commit them all at once later.
applied_successfully = patch.apply(reverse=reverse, index=commit, commit=False)
if not applied_successfully:
all_patches_applied = False
failed_patches.append(patch)
should_stop_now = not applied_successfully and stop_on_error
if should_stop_now:
break
if commit and not all_patches_applied:
git.reset(self.repo_path)
if commit and all_patches_applied:
author = 'Electron Build Process <build@electronjs.org>'
message = 'Apply Electron patches'
git.commit(self.repo_path, author=author, message=message)
return (all_patches_applied, failed_patches)
def reverse(self, stop_on_error=True, commit=False):
return self.apply(reverse=True, stop_on_error=stop_on_error, commit=commit)
class PatchesConfig:
@staticmethod
def from_directory(dir_path, project_root, config_name='.patches.yaml'):
config_path = os.path.join(dir_path, config_name)
return PatchesConfig(config_path, project_root)
def __init__(self, config_path, project_root):
self.path = config_path
self.patches_list = None
self.project_root = project_root
def __parse(self):
contents = None
if os.path.isfile(self.path):
with open(self.path, 'r') as stream:
try:
contents = yaml.load(stream)
except yaml.YAMLError as e:
print(e)
return contents
def __create_patch(self, raw_data, base_directory, repo_path, paths_prefix):
author = raw_data['author']
if author is None: # Shouldn't actually happen.
author = 'Anonymous <anonymous@electronjs.org>'
relative_file_path = raw_data['file']
absolute_file_path = os.path.join(base_directory, relative_file_path)
# Use a patch file path as a commit summary
# and optional description as a commit body.
description = relative_file_path
if raw_data['description'] is not None:
description += '\n\n' + raw_data['description']
return Patch(absolute_file_path, repo_path, paths_prefix=paths_prefix, author=author, description=description)
def __create_patches_list(self):
config_contents = self.__parse()
if config_contents is None:
return None
relative_repo_path = os.path.normpath(config_contents['repo'])
absolute_repo_path = os.path.join(self.project_root, relative_repo_path)
# If the 'repo' path is not really a git repository,
# then use that path as a prefix for patched files.
paths_prefix = None
if not git.is_repo_root(absolute_repo_path):
assert(git.is_repo_root(self.project_root))
absolute_repo_path = self.project_root
paths_prefix = relative_repo_path
patches_data = config_contents['patches']
base_directory = os.path.abspath(os.path.dirname(self.path))
patches = [self.__create_patch(data, base_directory, absolute_repo_path, paths_prefix) for data in patches_data]
patches_list = PatchesList(repo_path=absolute_repo_path, patches=patches)
return patches_list
def get_patches_list(self):
if self.patches_list is not None:
return self.patches_list
patches_list = self.__create_patches_list()
self.patches_list = patches_list
return patches_list