diff options
-rw-r--r-- | core/Makefile | 2 | ||||
-rw-r--r-- | core/combo/HOST_windows-x86.mk | 12 | ||||
-rw-r--r-- | core/config.mk | 2 | ||||
-rw-r--r-- | core/envsetup.mk | 4 | ||||
-rw-r--r-- | core/tasks/kernel.mk | 2 | ||||
-rw-r--r-- | envsetup.sh | 6 | ||||
-rwxr-xr-x | tools/repopick.py | 250 |
7 files changed, 270 insertions, 8 deletions
diff --git a/core/Makefile b/core/Makefile index 94b5561..cae5d1f 100644 --- a/core/Makefile +++ b/core/Makefile @@ -1660,7 +1660,7 @@ $(INTERNAL_SDK_TARGET): $(deps) MAIN_SDK_NAME := $(sdk_name) MAIN_SDK_DIR := $(sdk_dir) MAIN_SDK_ZIP := $(INTERNAL_SDK_TARGET) -ifneq ($(filter win_sdk,$(MAKECMDGOALS)),) +ifneq ($(filter win_sdk winsdk-coretools,$(MAKECMDGOALS)),) include $(TOPDIR)development/build/tools/windows_sdk.mk endif diff --git a/core/combo/HOST_windows-x86.mk b/core/combo/HOST_windows-x86.mk index 85bc1e0..bca6e31 100644 --- a/core/combo/HOST_windows-x86.mk +++ b/core/combo/HOST_windows-x86.mk @@ -23,22 +23,30 @@ TOOLS_PREFIX := #prebuilt/windows/host/bin/ TOOLS_EXE_SUFFIX := .exe # Settings to use MinGW has a cross-compiler under Linux -ifneq ($(findstring Linux,$(UNAME)),) ifneq ($(strip $(USE_MINGW)),) HOST_ACP_UNAVAILABLE := true TOOLS_EXE_SUFFIX := HOST_GLOBAL_CFLAGS += -DUSE_MINGW ifneq ($(strip $(BUILD_HOST_64bit)),) +ifeq ($(BUILD_OS),darwin) +$(error Windows amd64 cross compile builds not supported from Darwin) +else TOOLS_PREFIX := /usr/bin/amd64-mingw32msvc- HOST_C_INCLUDES += /usr/lib/gcc/amd64-mingw32msvc/4.4.2/include HOST_GLOBAL_LD_DIRS += -L/usr/amd64-mingw32msvc/lib +endif +else +ifeq ($(BUILD_OS),darwin) +TOOLS_PREFIX := /usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/bin//i586-mingw32- +HOST_C_INCLUDES += /usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/i586-mingw32/include /usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/i586-mingw32/include/ddk +HOST_GLOBAL_LD_DIRS += -L/usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/i586-mingw32/lib else TOOLS_PREFIX := /usr/bin/i586-mingw32msvc- HOST_C_INCLUDES += /usr/lib/gcc/i586-mingw32msvc/3.4.4/include HOST_GLOBAL_LD_DIRS += -L/usr/i586-mingw32msvc/lib +endif endif # BUILD_HOST_64bit endif # USE_MINGW -endif # Linux HOST_CC := $(TOOLS_PREFIX)gcc$(TOOLS_EXE_SUFFIX) HOST_CXX := $(TOOLS_PREFIX)g++$(TOOLS_EXE_SUFFIX) diff --git a/core/config.mk b/core/config.mk index ab06f6a..90237b7 100644 --- a/core/config.mk +++ b/core/config.mk @@ -336,7 +336,7 @@ endif OLD_FLEX := prebuilts/misc/$(HOST_PREBUILT_TAG)/flex/flex-2.5.4a$(HOST_EXECUTABLE_SUFFIX) -ifeq ($(HOST_OS),darwin) +ifeq ($(BUILD_OS),darwin) # Mac OS' screwy version of java uses a non-standard directory layout # and doesn't even seem to have tools.jar. On the other hand, javac seems # to be able to magically find the classes in there, wherever they are, so diff --git a/core/envsetup.mk b/core/envsetup.mk index 7a3ec92..1211887 100644 --- a/core/envsetup.mk +++ b/core/envsetup.mk @@ -55,13 +55,11 @@ endif # BUILD_OS is the real host doing the build. BUILD_OS := $(HOST_OS) -# Under Linux, if USE_MINGW is set, we change HOST_OS to Windows to build the +# If USE_MINGW is set, we change HOST_OS to Windows to build the # Windows SDK. Only a subset of tools and SDK will manage to build properly. -ifeq ($(HOST_OS),linux) ifneq ($(USE_MINGW),) HOST_OS := windows endif -endif ifeq ($(HOST_OS),) $(error Unable to determine HOST_OS from uname -sm: $(UNAME)!) diff --git a/core/tasks/kernel.mk b/core/tasks/kernel.mk index d3419a0..a6942ca 100644 --- a/core/tasks/kernel.mk +++ b/core/tasks/kernel.mk @@ -58,7 +58,7 @@ ifeq "$(wildcard $(KERNEL_SRC) )" "" $(warning * THIS IS DEPRECATED, AND WILL BE DISCONTINUED *) $(warning * Please configure your device to download the kernel *) $(warning * source repository to $(KERNEL_SRC)) - $(warning * See http://wiki.cyanogenmod.com/wiki/Integrated_kernel_building) + $(warning * See http://wiki.cyanogenmod.org/w/Doc:_integrated_kernel_building) $(warning * for more information *) $(warning ***************************************************************) FULL_KERNEL_BUILD := false diff --git a/envsetup.sh b/envsetup.sh index 51f3420..07c481d 100644 --- a/envsetup.sh +++ b/envsetup.sh @@ -22,6 +22,7 @@ Invoke ". build/envsetup.sh" from your shell to add the following functions to y - mkap: Builds the module(s) using mka and pushes them to the device. - cmka: Cleans and builds using mka. - reposync: Parallel repo sync using ionice and SCHED_BATCH. +- repopick: Utility to fetch changes from Gerrit. - installboot: Installs a boot.img to the connected device. - installrecovery: Installs a recovery.img to the connected device. @@ -1839,6 +1840,11 @@ alias mmmp='dopush mmm' alias mkap='dopush mka' alias cmkap='dopush cmka' +function repopick() { + T=$(gettop) + $T/build/tools/repopick.py $@ +} + # Force JAVA_HOME to point to java 1.6 if it isn't already set function set_java_home() { diff --git a/tools/repopick.py b/tools/repopick.py new file mode 100755 index 0000000..7336e78 --- /dev/null +++ b/tools/repopick.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python +# +# Copyright (C) 2013 The CyanogenMod Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Run repopick.py -h for a description of this utility. +# + +from __future__ import print_function + +import sys +import json +import os +import subprocess +import re +import argparse +import textwrap + +try: + # For python3 + import urllib.request +except ImportError: + # For python2 + import imp + import urllib2 + urllib = imp.new_module('urllib') + urllib.request = urllib2 + +# Parse the command line +parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent('''\ + repopick.py is a utility to simplify the process of cherry picking + patches from CyanogenMod's Gerrit instance. + + Given a list of change numbers, repopick will cd into the project path + and cherry pick the latest patch available. + + With the --start-branch argument, the user can specify that a branch + should be created before cherry picking. This is useful for + cherry-picking many patches into a common branch which can be easily + abandoned later (good for testing other's changes.) + + The --abandon-first argument, when used in conjuction with the + --start-branch option, will cause repopick to abandon the specified + branch in all repos first before performing any cherry picks.''')) +parser.add_argument('change_number', nargs='+', help='change number to cherry pick') +parser.add_argument('-i', '--ignore-missing', action='store_true', help='do not error out if a patch applies to a missing directory') +parser.add_argument('-s', '--start-branch', nargs=1, help='start the specified branch before cherry picking') +parser.add_argument('-a', '--abandon-first', action='store_true', help='before cherry picking, abandon the branch specified in --start-branch') +parser.add_argument('-b', '--auto-branch', action='store_true', help='shortcut to "--start-branch auto --abandon-first --ignore-missing"') +parser.add_argument('-q', '--quiet', action='store_true', help='print as little as possible') +parser.add_argument('-v', '--verbose', action='store_true', help='print extra information to aid in debug') +args = parser.parse_args() +if args.start_branch == None and args.abandon_first: + parser.error('if --abandon-first is set, you must also give the branch name with --start-branch') +if args.auto_branch: + args.abandon_first = True + args.ignore_missing = True + if not args.start_branch: + args.start_branch = ['auto'] +if args.quiet and args.verbose: + parser.error('--quiet and --verbose cannot be specified together') + +# Helper function to determine whether a path is an executable file +def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +# Implementation of Unix 'which' in Python +# +# From: http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python +def which(program): + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + return None + +# Simple wrapper for os.system() that: +# - exits on error +# - prints out the command if --verbose +# - suppresses all output if --quiet +def execute_cmd(cmd): + if args.verbose: + print('Executing: %s' % cmd) + if args.quiet: + cmd = cmd.replace(' && ', ' &> /dev/null && ') + cmd = cmd + " &> /dev/null" + if os.system(cmd): + if not args.verbose: + print('\nCommand that failed:\n%s' % cmd) + sys.exit(1) + +# Verifies whether pathA is a subdirectory (or the same) as pathB +def is_pathA_subdir_of_pathB(pathA, pathB): + pathA = os.path.realpath(pathA) + '/' + pathB = os.path.realpath(pathB) + '/' + return(pathB == pathA[:len(pathB)]) + +# Find the necessary bins - repo +repo_bin = which('repo') +if repo_bin == None: + repo_bin = os.path.join(os.environ["HOME"], 'repo') + if not is_exe(repo_bin): + sys.stderr.write('ERROR: Could not find the repo program in either $PATH or $HOME/bin\n') + sys.exit(1) + +# Find the necessary bins - git +git_bin = which('git') +if not is_exe(git_bin): + sys.stderr.write('ERROR: Could not find the git program in $PATH\n') + sys.exit(1) + +# Change current directory to the top of the tree +if 'ANDROID_BUILD_TOP' in os.environ: + top = os.environ['ANDROID_BUILD_TOP'] + if not is_pathA_subdir_of_pathB(os.getcwd(), top): + sys.stderr.write('ERROR: You must run this tool from within $ANDROID_BUILD_TOP!\n') + sys.exit(1) + os.chdir(os.environ['ANDROID_BUILD_TOP']) + +# Sanity check that we are being run from the top level of the tree +if not os.path.isdir('.repo'): + sys.stderr.write('ERROR: No .repo directory found. Please run this from the top of your tree.\n') + sys.exit(1) + +# If --abandon-first is given, abandon the branch before starting +if args.abandon_first: + # Determine if the branch already exists; skip the abandon if it does not + plist = subprocess.Popen([repo_bin,"info"], stdout=subprocess.PIPE) + needs_abandon = False + while(True): + pline = plist.stdout.readline().rstrip() + if not pline: + break + matchObj = re.match(r'Local Branches.*\[(.*)\]', pline.decode()) + if matchObj: + local_branches = re.split('\s*,\s*', matchObj.group(1)) + if any(args.start_branch[0] in s for s in local_branches): + needs_abandon = True + break + + if needs_abandon: + # Perform the abandon only if the branch already exists + if not args.quiet: + print('Abandoning branch: %s' % args.start_branch[0]) + cmd = '%s abandon %s' % (repo_bin, args.start_branch[0]) + execute_cmd(cmd) + if not args.quiet: + print('') + +# Iterate through the requested change numbers +for change in args.change_number: + if not args.quiet: + print('Applying change number %s ...' % change) + + # Fetch information about the change from Gerrit's REST API + # + # gerrit returns two lines, a magic string and then valid JSON: + # )]}' + # [ ... valid JSON ... ] + url = 'http://review.cyanogenmod.org/changes/?q=%s&o=CURRENT_REVISION&o=CURRENT_COMMIT&pp=0' % change + if args.verbose: + print('Fetching from: %s\n' % url) + f = urllib.request.urlopen(url) + d = f.read().decode("utf-8") + + # Parse the result + if args.verbose: + print('Result from request:\n' + d) + d = d.split('\n')[1] + d = re.sub(r'\[(.*)\]', r'\1', d) + data = json.loads(d) + + # Extract information from the JSON response + project_name = data['project'] + change_number = data['_number'] + current_revision = data['revisions'][data['current_revision']] + patch_number = current_revision['_number'] + fetch_url = current_revision['fetch']['http']['url'] + fetch_ref = current_revision['fetch']['http']['ref'] + author_name = current_revision['commit']['author']['name'] + author_email = current_revision['commit']['author']['email'] + author_date = current_revision['commit']['author']['date'] + committer_name = current_revision['commit']['committer']['name'] + committer_email = current_revision['commit']['committer']['email'] + committer_date = current_revision['commit']['committer']['date'] + subject = current_revision['commit']['subject'] + + # Get the list of projects that repo knows about + # - convert the project name to a project path + plist = subprocess.Popen([repo_bin,"list"], stdout=subprocess.PIPE) + while(True): + pline = plist.stdout.readline().rstrip() + if not pline: + break + ppaths = re.split('\s*:\s*', pline.decode()) + if ppaths[1] == project_name: + project_path = ppaths[0] + break + if 'project_path' not in locals(): + sys.stderr.write('ERROR: Could not determine the project path for project %s\n' % project_name) + sys.exit(1) + + # Check that the project path exists + if not os.path.isdir(project_path): + if args.ignore_missing: + print('WARNING: Skipping %d since there is no project directory: %s\n' % (change_number, project_path)) + continue; + else: + sys.stderr.write('ERROR: For %d, there is no project directory: %s\n' % (change_number, project_path)) + sys.exit(1) + + # If --start-branch is given, create the branch (more than once per path is okay; repo ignores gracefully) + if args.start_branch: + cmd = '%s start %s %s' % (repo_bin, args.start_branch[0], project_path) + execute_cmd(cmd) + + # Print out some useful info + if not args.quiet: + print('--> Subject: "%s"' % subject) + print('--> Project path: %s' % project_path) + print('--> Change number: %d (Patch Set %d)' % (change_number, patch_number)) + print('--> Author: %s <%s> %s' % (author_name, author_email, author_date)) + print('--> Committer: %s <%s> %s' % (committer_name, committer_email, committer_date)) + + # Perform the cherry-pick + cmd = 'cd %s && git fetch %s %s && git cherry-pick FETCH_HEAD' % (project_path, fetch_url, fetch_ref) + execute_cmd(cmd) + if not args.quiet: + print('') + |