summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/apicheck/Android.mk2
-rwxr-xr-xtools/buildinfo.sh23
-rw-r--r--tools/device/AndroidBoard.mk.template8
-rw-r--r--tools/device/AndroidProducts.mk.template2
-rw-r--r--tools/device/BoardConfig.mk.template31
-rw-r--r--tools/device/cm.mk.template15
-rw-r--r--tools/device/device.mk.template24
-rw-r--r--tools/device/makerecoveries.sh69
-rwxr-xr-xtools/device/mkrecoveryzip.sh97
-rwxr-xr-xtools/device/mkvendor.sh115
-rw-r--r--tools/device/recovery.fstab.template10
-rw-r--r--tools/device/system.prop.template3
-rwxr-xr-xtools/getb64key.py17
-rwxr-xr-xtools/post_process_props.py25
-rwxr-xr-xtools/releasetools/add_img_to_target_files.py38
-rwxr-xr-xtools/releasetools/build_image.py15
-rw-r--r--tools/releasetools/common.py123
-rw-r--r--tools/releasetools/edify_generator.py58
-rwxr-xr-xtools/releasetools/img_from_target_files.py26
-rwxr-xr-xtools/releasetools/ota_from_target_files.py170
-rwxr-xr-xtools/repopick.py355
-rwxr-xr-xtools/roomservice.py295
22 files changed, 1403 insertions, 118 deletions
diff --git a/tools/apicheck/Android.mk b/tools/apicheck/Android.mk
index 1674a17..1505c8d 100644
--- a/tools/apicheck/Android.mk
+++ b/tools/apicheck/Android.mk
@@ -32,7 +32,7 @@ include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/doclava$(COMMON_JAVA_PACKAGE_SUFFIX)
$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/apicheck | $(ACP)
- @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ @echo -e ${CL_CYN}"Copy:"${CL_RST}" $(PRIVATE_MODULE) ($@)"
$(copy-file-to-new-target)
$(hide) chmod 755 $@
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
index 5c199b8..559cd88 100755
--- a/tools/buildinfo.sh
+++ b/tools/buildinfo.sh
@@ -20,10 +20,8 @@ echo "ro.build.user=$USER"
echo "ro.build.host=`hostname`"
echo "ro.build.tags=$BUILD_VERSION_TAGS"
echo "ro.build.flavor=$TARGET_BUILD_FLAVOR"
-echo "ro.product.model=$PRODUCT_MODEL"
echo "ro.product.brand=$PRODUCT_BRAND"
echo "ro.product.name=$PRODUCT_NAME"
-echo "ro.product.device=$TARGET_DEVICE"
echo "ro.product.board=$TARGET_BOOTLOADER_BOARD_NAME"
# These values are deprecated, use "ro.product.cpu.abilist"
@@ -45,15 +43,20 @@ fi
echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS"
echo "ro.board.platform=$TARGET_BOARD_PLATFORM"
-echo "# ro.build.product is obsolete; use ro.product.device"
-echo "ro.build.product=$TARGET_DEVICE"
-
-echo "# Do not try to parse description, fingerprint, or thumbprint"
-echo "ro.build.description=$PRIVATE_BUILD_DESC"
-echo "ro.build.fingerprint=$BUILD_FINGERPRINT"
-if [ -n "$BUILD_THUMBPRINT" ] ; then
- echo "ro.build.thumbprint=$BUILD_THUMBPRINT"
+if [ "$TARGET_UNIFIED_DEVICE" == "" ] ; then
+ echo "# ro.build.product is obsolete; use ro.product.device"
+ echo "ro.build.product=$TARGET_DEVICE"
+ echo "ro.product.model=$PRODUCT_MODEL"
+ echo "ro.product.device=$TARGET_DEVICE"
+ echo "# Do not try to parse description, fingerprint, or thumbprint"
+ echo "ro.build.description=$PRIVATE_BUILD_DESC"
+ echo "ro.build.fingerprint=$BUILD_FINGERPRINT"
+ if [ -n "$BUILD_THUMBPRINT" ] ; then
+ echo "ro.build.thumbprint=$BUILD_THUMBPRINT"
+ fi
fi
echo "ro.build.characteristics=$TARGET_AAPT_CHARACTERISTICS"
+echo "ro.cm.device=$CM_DEVICE"
+
echo "# end build properties"
diff --git a/tools/device/AndroidBoard.mk.template b/tools/device/AndroidBoard.mk.template
new file mode 100644
index 0000000..55a36d5
--- /dev/null
+++ b/tools/device/AndroidBoard.mk.template
@@ -0,0 +1,8 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+ALL_PREBUILT += $(INSTALLED_KERNEL_TARGET)
+
+# include the non-open-source counterpart to this file
+-include vendor/__MANUFACTURER__/__DEVICE__/AndroidBoardVendor.mk
diff --git a/tools/device/AndroidProducts.mk.template b/tools/device/AndroidProducts.mk.template
new file mode 100644
index 0000000..f31c5bf
--- /dev/null
+++ b/tools/device/AndroidProducts.mk.template
@@ -0,0 +1,2 @@
+PRODUCT_MAKEFILES := \
+ $(LOCAL_DIR)/device___DEVICE__.mk
diff --git a/tools/device/BoardConfig.mk.template b/tools/device/BoardConfig.mk.template
new file mode 100644
index 0000000..617673f
--- /dev/null
+++ b/tools/device/BoardConfig.mk.template
@@ -0,0 +1,31 @@
+USE_CAMERA_STUB := true
+
+# inherit from the proprietary version
+-include vendor/__MANUFACTURER__/__DEVICE__/BoardConfigVendor.mk
+
+TARGET_ARCH := arm
+TARGET_NO_BOOTLOADER := true
+TARGET_BOARD_PLATFORM := unknown
+TARGET_CPU_ABI := armeabi-v7a
+TARGET_CPU_ABI2 := armeabi
+TARGET_ARCH_VARIANT := armv7-a-neon
+TARGET_CPU_VARIANT := cortex-a7
+TARGET_CPU_SMP := true
+ARCH_ARM_HAVE_TLS_REGISTER := true
+
+TARGET_BOOTLOADER_BOARD_NAME := __DEVICE__
+
+BOARD_KERNEL_CMDLINE := __CMDLINE__
+BOARD_KERNEL_BASE := 0x__BASE__
+BOARD_KERNEL_PAGESIZE := __PAGE_SIZE__
+
+# fix this up by examining /proc/mtd on a running device
+BOARD_BOOTIMAGE_PARTITION_SIZE := 0x00380000
+BOARD_RECOVERYIMAGE_PARTITION_SIZE := 0x00480000
+BOARD_SYSTEMIMAGE_PARTITION_SIZE := 0x08c60000
+BOARD_USERDATAIMAGE_PARTITION_SIZE := 0x105c0000
+BOARD_FLASH_BLOCK_SIZE := 131072
+
+TARGET_PREBUILT_KERNEL := device/__MANUFACTURER__/__DEVICE__/kernel
+
+BOARD_HAS_NO_SELECT_BUTTON := true
diff --git a/tools/device/cm.mk.template b/tools/device/cm.mk.template
new file mode 100644
index 0000000..e07898a
--- /dev/null
+++ b/tools/device/cm.mk.template
@@ -0,0 +1,15 @@
+# Release name
+PRODUCT_RELEASE_NAME := __DEVICE__
+
+# Inherit some common CM stuff.
+$(call inherit-product, vendor/cm/config/common_full_phone.mk)
+
+# Inherit device configuration
+$(call inherit-product, device/__MANUFACTURER__/__DEVICE__/device___DEVICE__.mk)
+
+## Device identifier. This must come after all inclusions
+PRODUCT_DEVICE := __DEVICE__
+PRODUCT_NAME := cm___DEVICE__
+PRODUCT_BRAND := __MANUFACTURER__
+PRODUCT_MODEL := __DEVICE__
+PRODUCT_MANUFACTURER := __MANUFACTURER__
diff --git a/tools/device/device.mk.template b/tools/device/device.mk.template
new file mode 100644
index 0000000..91ffdc9
--- /dev/null
+++ b/tools/device/device.mk.template
@@ -0,0 +1,24 @@
+$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_full.mk)
+
+# The gps config appropriate for this device
+$(call inherit-product, device/common/gps/gps_us_supl.mk)
+
+$(call inherit-product-if-exists, vendor/__MANUFACTURER__/__DEVICE__/__DEVICE__-vendor.mk)
+
+DEVICE_PACKAGE_OVERLAYS += device/__MANUFACTURER__/__DEVICE__/overlay
+
+
+ifeq ($(TARGET_PREBUILT_KERNEL),)
+ LOCAL_KERNEL := device/__MANUFACTURER__/__DEVICE__/kernel
+else
+ LOCAL_KERNEL := $(TARGET_PREBUILT_KERNEL)
+endif
+
+PRODUCT_COPY_FILES += \
+ $(LOCAL_KERNEL):kernel
+
+$(call inherit-product, build/target/product/full.mk)
+
+PRODUCT_BUILD_PROP_OVERRIDES += BUILD_UTC_DATE=0
+PRODUCT_NAME := full___DEVICE__
+PRODUCT_DEVICE := __DEVICE__
diff --git a/tools/device/makerecoveries.sh b/tools/device/makerecoveries.sh
new file mode 100644
index 0000000..f561c81
--- /dev/null
+++ b/tools/device/makerecoveries.sh
@@ -0,0 +1,69 @@
+if [ -z "$1" ]
+then
+ echo "Please provide a lunch option."
+ return
+fi
+
+PRODUCTS=$1
+
+for product in $PRODUCTS
+do
+ echo $product
+done
+
+echo $(echo $PRODUCTS | wc -w) Products
+
+unset PUBLISHED_RECOVERIES
+
+MCP=$(which mcp)
+if [ -z "$MCP" ]
+then
+ NO_UPLOAD=true
+fi
+
+function mcpguard () {
+ if [ -z $NO_UPLOAD ]
+ then
+ mcp $1 $2
+ md5sum $1 > $1.md5sum.txt
+ mcp $1.md5sum.txt $2.md5sum.txt
+ fi
+}
+
+VERSION=$(cat bootable/recovery/Android.mk | grep RECOVERY_VERSION | grep RECOVERY_NAME | awk '{ print $4 }' | sed s/v//g)
+echo Recovery Version: $VERSION
+
+for lunchoption in $PRODUCTS
+do
+ lunch $lunchoption
+ if [ -z $NO_CLEAN ]
+ then
+ rm -rf $OUT/obj/EXECUTABLES/recovery_intermediates
+ rm -rf $OUT/recovery*
+ rm -rf $OUT/root*
+ fi
+ DEVICE_NAME=$(echo $TARGET_PRODUCT | sed s/koush_// | sed s/aosp_// | sed s/motorola// | sed s/huawei_// | sed s/htc_// | sed s/_us// | sed s/cyanogen_// | sed s/generic_// | sed s/full_//)
+ PRODUCT_NAME=$(basename $OUT)
+ make -j16 recoveryzip
+ RESULT=$?
+ if [ $RESULT != "0" ]
+ then
+ echo build error!
+ break
+ fi
+ mcpguard $OUT/recovery.img recoveries/recovery-clockwork-$VERSION-$DEVICE_NAME.img
+ mcpguard $OUT/utilities/update.zip recoveries/recovery-clockwork-$VERSION-$DEVICE_NAME.zip
+
+ if [ -f "ROMManagerManifest/devices.rb" ]
+ then
+ pushd ROMManagerManifest
+ ruby devices.rb $DEVICE_NAME $VERSION $lunchoption
+ popd
+ fi
+done
+
+for published_recovery in $PUBLISHED_RECOVERIES
+do
+ echo $published_recovery
+done
+
diff --git a/tools/device/mkrecoveryzip.sh b/tools/device/mkrecoveryzip.sh
new file mode 100755
index 0000000..e6fae37
--- /dev/null
+++ b/tools/device/mkrecoveryzip.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+OUT=$1
+SIGNAPK=$2
+
+if [ -z "$OUT" -o -z "$SIGNAPK" ]
+then
+ echo "Android build environment not detected."
+ exit 1
+fi
+
+ANDROID_ROOT=$(pwd)
+OUT=$ANDROID_ROOT/$OUT
+SIGNAPK=$ANDROID_ROOT/$SIGNAPK
+
+pushd . > /dev/null 2> /dev/null
+
+UTILITIES_DIR=$OUT/utilities
+mkdir -p $UTILITIES_DIR
+RECOVERY_DIR=$UTILITIES_DIR/recovery
+rm -rf $RECOVERY_DIR
+mkdir -p $RECOVERY_DIR
+cd $RECOVERY_DIR
+cp -R $OUT/recovery/root/etc etc
+cp -R $OUT/recovery/root/sbin sbin
+cp -R $OUT/recovery/root/res res
+SCRIPT_DIR=META-INF/com/google/android
+mkdir -p $SCRIPT_DIR
+cp $OUT/system/bin/updater $SCRIPT_DIR/update-binary
+
+
+UPDATER_SCRIPT=$SCRIPT_DIR/updater-script
+rm -f $UPDATER_SCRIPT
+touch $UPDATER_SCRIPT
+mkdir -p $(dirname $UPDATER_SCRIPT)
+
+FILES=
+SYMLINKS=
+
+for file in $(find .)
+do
+
+if [ -d $file ]
+then
+ continue
+fi
+
+META_INF=$(echo $file | grep META-INF)
+if [ ! -z $META_INF ]
+then
+ continue;
+fi
+
+if [ -h $file ]
+then
+ SYMLINKS=$SYMLINKS' '$file
+elif [ -f $file ]
+then
+ FILES=$FILES' '$file
+fi
+done
+
+
+echo 'ui_print("Replacing stock recovery with ClockworkMod recovery...");' >> $UPDATER_SCRIPT
+
+echo 'delete("sbin/recovery");' >> $UPDATER_SCRIPT
+echo 'package_extract_file("sbin/recovery", "/sbin/recovery");' >> $UPDATER_SCRIPT
+echo 'set_perm(0, 0, 0755, "/sbin/recovery");' >> $UPDATER_SCRIPT
+echo 'symlink("recovery", "/sbin/busybox");' >> $UPDATER_SCRIPT
+
+echo 'run_program("/sbin/busybox", "sh", "-c", "busybox rm -f /etc ; busybox mkdir -p /etc;");' >> $UPDATER_SCRIPT
+
+for file in $FILES
+do
+ echo 'delete("'$(echo $file | sed s!\\./!!g)'");' >> $UPDATER_SCRIPT
+ echo 'package_extract_file("'$(echo $file | sed s!\\./!!g)'", "'$(echo $file | sed s!\\./!/!g)'");' >> $UPDATER_SCRIPT
+ if [ -x $file ]
+ then
+ echo 'set_perm(0, 0, 0755, "'$(echo $file | sed s!\\./!/!g)'");' >> $UPDATER_SCRIPT
+ fi
+done
+
+for file in $SYMLINKS
+do
+ echo 'symlink("'$(readlink $file)'", "'$(echo $file | sed s!\\./!/!g)'");' >> $UPDATER_SCRIPT
+done
+
+echo 'set_perm_recursive(0, 2000, 0755, 0755, "/sbin");' >> $UPDATER_SCRIPT
+echo 'run_program("/sbin/busybox", "sh", "-c", "/sbin/killrecovery.sh");' >> $UPDATER_SCRIPT
+rm -f $UTILITIES_DIR/unsigned.zip
+rm -f $UTILITIES_DIR/update.zip
+echo zip -ry $UTILITIES_DIR/unsigned.zip . -x $SYMLINKS '*\[*' '*\[\[*'
+zip -ry $UTILITIES_DIR/unsigned.zip . -x $SYMLINKS '*\[*' '*\[\[*'
+java -jar $SIGNAPK -w $ANDROID_ROOT/build/target/product/security/testkey.x509.pem $ANDROID_ROOT/build/target/product/security/testkey.pk8 $UTILITIES_DIR/unsigned.zip $UTILITIES_DIR/update.zip
+
+echo Recovery FakeFlash is now available at $OUT/utilities/update.zip
+popd > /dev/null 2> /dev/null
diff --git a/tools/device/mkvendor.sh b/tools/device/mkvendor.sh
new file mode 100755
index 0000000..778d82d
--- /dev/null
+++ b/tools/device/mkvendor.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+
+function usage
+{
+ echo Usage:
+ echo " $(basename $0) manufacturer device [boot.img]"
+ echo " The boot.img argument is the extracted recovery or boot image."
+ echo " The boot.img argument should not be provided for devices"
+ echo " that have non standard boot images (ie, Samsung)."
+ echo
+ echo Example:
+ echo " $(basename $0) motorola sholes ~/Downloads/recovery-sholes.img"
+ exit 0
+}
+
+MANUFACTURER=$1
+DEVICE=$2
+BOOTIMAGE=$3
+
+UNPACKBOOTIMG=$(which unpackbootimg)
+
+echo Arguments: $@
+
+if [ -z "$MANUFACTURER" ]
+then
+ usage
+fi
+
+if [ -z "$DEVICE" ]
+then
+ usage
+fi
+
+ANDROID_TOP=$(dirname $0)/../../../
+pushd $ANDROID_TOP > /dev/null
+ANDROID_TOP=$(pwd)
+popd > /dev/null
+
+TEMPLATE_DIR=$(dirname $0)
+pushd $TEMPLATE_DIR > /dev/null
+TEMPLATE_DIR=$(pwd)
+popd > /dev/null
+
+DEVICE_DIR=$ANDROID_TOP/device/$MANUFACTURER/$DEVICE
+
+if [ ! -z "$BOOTIMAGE" ]
+then
+ if [ -z "$UNPACKBOOTIMG" ]
+ then
+ echo unpackbootimg not found. Is your android build environment set up and have the host tools been built?
+ exit 0
+ fi
+
+ BOOTIMAGEFILE=$(basename $BOOTIMAGE)
+
+ echo Output will be in $DEVICE_DIR
+ mkdir -p $DEVICE_DIR
+
+ TMPDIR=/tmp/$(whoami)/bootimg
+ rm -rf $TMPDIR
+ mkdir -p $TMPDIR
+ cp $BOOTIMAGE $TMPDIR
+ pushd $TMPDIR > /dev/null
+ unpackbootimg -i $BOOTIMAGEFILE > /dev/null
+ mkdir ramdisk
+ pushd ramdisk > /dev/null
+ gunzip -c ../$BOOTIMAGEFILE-ramdisk.gz | cpio -i
+ popd > /dev/null
+ BASE=$(cat $TMPDIR/$BOOTIMAGEFILE-base)
+ CMDLINE=$(cat $TMPDIR/$BOOTIMAGEFILE-cmdline)
+ PAGESIZE=$(cat $TMPDIR/$BOOTIMAGEFILE-pagesize)
+ export SEDCMD="s#__CMDLINE__#$CMDLINE#g"
+ echo $SEDCMD > $TMPDIR/sedcommand
+ cp $TMPDIR/$BOOTIMAGEFILE-zImage $DEVICE_DIR/kernel
+ popd > /dev/null
+else
+ mkdir -p $DEVICE_DIR
+ touch $DEVICE_DIR/kernel
+ BASE=10000000
+ CMDLINE=no_console_suspend
+ PAGESIZE=00000800
+ export SEDCMD="s#__CMDLINE__#$CMDLINE#g"
+ echo $SEDCMD > $TMPDIR/sedcommand
+fi
+
+for file in $(find $TEMPLATE_DIR -name '*.template')
+do
+ OUTPUT_FILE=$DEVICE_DIR/$(basename $(echo $file | sed s/\\.template//g))
+ cat $file | sed s/__DEVICE__/$DEVICE/g | sed s/__MANUFACTURER__/$MANUFACTURER/g | sed -f $TMPDIR/sedcommand | sed s/__BASE__/$BASE/g | sed s/__PAGE_SIZE__/$PAGESIZE/g > $OUTPUT_FILE
+done
+
+if [ ! -z "$TMPDIR" ]
+then
+ RECOVERY_FSTAB=$TMPDIR/ramdisk/etc/recovery.fstab
+ if [ -f "$RECOVERY_FSTAB" ]
+ then
+ cp $RECOVERY_FSTAB $DEVICE_DIR/recovery.fstab
+ fi
+fi
+
+
+mv $DEVICE_DIR/device.mk $DEVICE_DIR/device_$DEVICE.mk
+
+echo Creating initial git repository.
+pushd $DEVICE_DIR
+git init
+git add .
+git commit -a -m "mkvendor.sh: Initial commit of $DEVICE"
+popd
+
+echo Done!
+echo Use the following command to set up your build environment:
+echo ' 'lunch cm_$DEVICE-eng
+echo And use the follwowing command to build a recovery:
+echo ' '. build/tools/device/makerecoveries.sh cm_$DEVICE-eng
diff --git a/tools/device/recovery.fstab.template b/tools/device/recovery.fstab.template
new file mode 100644
index 0000000..41fb92e
--- /dev/null
+++ b/tools/device/recovery.fstab.template
@@ -0,0 +1,10 @@
+# mount point fstype device [device2]
+
+/boot mtd boot
+/cache yaffs2 cache
+/data yaffs2 userdata
+/misc mtd misc
+/recovery mtd recovery
+/sdcard vfat /dev/block/mmcblk0p1 /dev/block/mmcblk0
+/system yaffs2 system
+/sd-ext ext4 /dev/block/mmcblk0p2
diff --git a/tools/device/system.prop.template b/tools/device/system.prop.template
new file mode 100644
index 0000000..4113929
--- /dev/null
+++ b/tools/device/system.prop.template
@@ -0,0 +1,3 @@
+#
+# system.prop for __DEVICE__
+#
diff --git a/tools/getb64key.py b/tools/getb64key.py
new file mode 100755
index 0000000..a0cd1c3
--- /dev/null
+++ b/tools/getb64key.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+import base64
+import sys
+import os
+
+pkFile = open(sys.argv[1], 'rb').readlines()
+base64Key = ""
+inCert = False
+for line in pkFile:
+ if line.startswith("-"):
+ inCert = not inCert
+ continue
+
+ base64Key += line.strip()
+
+print base64.b16encode(base64.b64decode(base64Key)).lower()
diff --git a/tools/post_process_props.py b/tools/post_process_props.py
index fa6106f..5193d4b 100755
--- a/tools/post_process_props.py
+++ b/tools/post_process_props.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
+import os, sys
# Usage: post_process_props.py file.prop [blacklist_key, ...]
# Blacklisted keys are removed from the property file, if present
@@ -27,15 +27,22 @@ PROP_VALUE_MAX = 91
# Put the modifications that you need to make into the /system/build.prop into this
# function. The prop object has get(name) and put(name,value) methods.
-def mangle_build_prop(prop):
+def mangle_build_prop(prop, overrides):
+ if len(overrides) == 0:
+ return
+ overridelist = overrides.replace(" ",",").split(",")
+ for proppair in overridelist:
+ values = proppair.split("=")
+ prop.put(values[0], values[1])
+
pass
# Put the modifications that you need to make into the /default.prop into this
# function. The prop object has get(name) and put(name,value) methods.
def mangle_default_prop(prop):
- # If ro.debuggable is 1, then enable adb on USB by default
- # (this is for userdebug builds)
- if prop.get("ro.debuggable") == "1":
+ # If ro.adb.secure is not 1, then enable adb on USB by default
+ # (this is for eng builds)
+ if prop.get("ro.adb.secure") != "1":
val = prop.get("persist.sys.usb.config")
if val == "":
val = "adb"
@@ -110,6 +117,10 @@ class PropFile:
def main(argv):
filename = argv[1]
+ if (len(argv) > 2):
+ extraargs = argv[2]
+ else:
+ extraargs = ""
f = open(filename)
lines = f.readlines()
f.close()
@@ -117,7 +128,7 @@ def main(argv):
properties = PropFile(lines)
if filename.endswith("/build.prop"):
- mangle_build_prop(properties)
+ mangle_build_prop(properties, extraargs)
elif filename.endswith("/default.prop"):
mangle_default_prop(properties)
else:
@@ -128,7 +139,7 @@ def main(argv):
sys.exit(1)
# Drop any blacklisted keys
- for key in argv[2:]:
+ for key in argv[3:]:
properties.delete(key)
f = open(filename, 'w+')
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index f7332b2..e8d61ad 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -194,6 +194,42 @@ def AddUserdata(output_zip, prefix="IMAGES/"):
os.rmdir(temp_dir)
+def AddUserdataExtra(output_zip):
+ """Create extra userdata image and store it in output_zip."""
+
+ image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
+ "data_extra")
+ # If no userdataextra_size is provided for extfs, skip userdata_extra.img.
+ if (image_props.get("fs_type", "").startswith("ext") and
+ not image_props.get("partition_size")):
+ return
+
+ extra_name = image_props.get("partition_name", "extra")
+
+ print "creating userdata_%s.img..." % extra_name
+
+ # The name of the directory it is making an image out of matters to
+ # mkyaffs2image. So we create a temp dir, and within it we create an
+ # empty dir named "data", and build the image from that.
+ temp_dir = tempfile.mkdtemp()
+ user_dir = os.path.join(temp_dir, "data")
+ os.mkdir(user_dir)
+ img = tempfile.NamedTemporaryFile()
+
+ fstab = OPTIONS.info_dict["fstab"]
+ if fstab:
+ image_props["fs_type" ] = fstab["/data"].fs_type
+ succ = build_image.BuildImage(user_dir, image_props, img.name)
+ assert succ, "build userdata_%s.img image failed" % extra_name
+
+ # Disable size check since this fetches original data partition size
+ #common.CheckSize(img.name, "userdata_extra.img", OPTIONS.info_dict)
+ output_zip.write(img.name, "userdata_%s.img" % extra_name)
+ img.close()
+ os.rmdir(user_dir)
+ os.rmdir(temp_dir)
+
+
def AddCache(output_zip, prefix="IMAGES/"):
"""Create an empty cache image and store it in output_zip."""
@@ -293,6 +329,8 @@ def AddImagesToTargetFiles(filename):
AddVendor(output_zip)
banner("userdata")
AddUserdata(output_zip)
+ banner("extrauserdata")
+ AddUserdataExtra(output_zip)
banner("cache")
AddCache(output_zip)
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 4b43c0c..91fe54b 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -263,9 +263,13 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
build_command = ["mkuserimg.sh"]
if "extfs_sparse_flag" in prop_dict:
build_command.append(prop_dict["extfs_sparse_flag"])
- run_fsck = True
- build_command.extend([in_dir, out_file, fs_type,
- prop_dict["mount_point"]])
+ #run_fsck = True
+ if "is_userdataextra" in prop_dict:
+ build_command.extend([in_dir, out_file, fs_type,
+ "data"])
+ else:
+ build_command.extend([in_dir, out_file, fs_type,
+ prop_dict["mount_point"]])
build_command.append(prop_dict["partition_size"])
if "journal_size" in prop_dict:
build_command.extend(["-j", prop_dict["journal_size"]])
@@ -439,6 +443,11 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
copy_prop("fs_type", "fs_type")
copy_prop("userdata_fs_type", "fs_type")
copy_prop("userdata_size", "partition_size")
+ elif mount_point == "data_extra":
+ copy_prop("fs_type", "fs_type")
+ copy_prop("userdataextra_size", "partition_size")
+ copy_prop("userdataextra_name", "partition_name")
+ d["is_userdataextra"] = True
elif mount_point == "cache":
copy_prop("cache_fs_type", "fs_type")
copy_prop("cache_size", "partition_size")
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index ebf47cb..d13f82a 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -148,6 +148,8 @@ def LoadInfoDict(input_file):
if "fstab_version" not in d:
d["fstab_version"] = "1"
+ if "device_type" not in d:
+ d["device_type"] = "MMC"
try:
data = read_helper("META/imagesizes.txt")
for line in data.split("\n"):
@@ -177,7 +179,7 @@ def LoadInfoDict(input_file):
makeint("boot_size")
makeint("fstab_version")
- d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"])
+ d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"], d["device_type"])
d["build.prop"] = LoadBuildProp(read_helper)
return d
@@ -200,7 +202,7 @@ def LoadDictionaryFromLines(lines):
d[name] = value
return d
-def LoadRecoveryFSTab(read_helper, fstab_version):
+def LoadRecoveryFSTab(read_helper, fstab_version, type):
class Partition(object):
def __init__(self, mount_point, fs_type, device, length, device2, context):
self.mount_point = mount_point
@@ -329,44 +331,74 @@ def BuildBootableImage(sourcedir, fs_config_file, info_dict=None):
assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
- # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
- mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
-
- cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
-
- fn = os.path.join(sourcedir, "second")
- if os.access(fn, os.F_OK):
- cmd.append("--second")
- cmd.append(fn)
-
- fn = os.path.join(sourcedir, "cmdline")
- if os.access(fn, os.F_OK):
- cmd.append("--cmdline")
- cmd.append(open(fn).read().rstrip("\n"))
-
- fn = os.path.join(sourcedir, "base")
- if os.access(fn, os.F_OK):
- cmd.append("--base")
- cmd.append(open(fn).read().rstrip("\n"))
-
- fn = os.path.join(sourcedir, "pagesize")
+ """check if uboot is requested"""
+ fn = os.path.join(sourcedir, "ubootargs")
if os.access(fn, os.F_OK):
- cmd.append("--pagesize")
- cmd.append(open(fn).read().rstrip("\n"))
+ cmd = ["mkimage"]
+ for argument in open(fn).read().rstrip("\n").split(" "):
+ cmd.append(argument)
+ cmd.append("-d")
+ cmd.append(os.path.join(sourcedir, "kernel")+":"+ramdisk_img.name)
+ cmd.append(img.name)
- args = info_dict.get("mkbootimg_args", None)
- if args and args.strip():
- cmd.extend(shlex.split(args))
-
- img_unsigned = None
- if info_dict.get("vboot", None):
- img_unsigned = tempfile.NamedTemporaryFile()
- cmd.extend(["--ramdisk", ramdisk_img.name,
- "--output", img_unsigned.name])
else:
- cmd.extend(["--ramdisk", ramdisk_img.name,
+ # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
+ mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
+ cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
+
+ fn = os.path.join(sourcedir, "second")
+ if os.access(fn, os.F_OK):
+ cmd.append("--second")
+ cmd.append(fn)
+
+ fn = os.path.join(sourcedir, "cmdline")
+ if os.access(fn, os.F_OK):
+ cmd.append("--cmdline")
+ cmd.append(open(fn).read().rstrip("\n"))
+
+ fn = os.path.join(sourcedir, "base")
+ if os.access(fn, os.F_OK):
+ cmd.append("--base")
+ cmd.append(open(fn).read().rstrip("\n"))
+
+ fn = os.path.join(sourcedir, "tagsaddr")
+ if os.access(fn, os.F_OK):
+ cmd.append("--tags-addr")
+ cmd.append(open(fn).read().rstrip("\n"))
+
+ fn = os.path.join(sourcedir, "tags_offset")
+ if os.access(fn, os.F_OK):
+ cmd.append("--tags_offset")
+ cmd.append(open(fn).read().rstrip("\n"))
+
+ fn = os.path.join(sourcedir, "ramdisk_offset")
+ if os.access(fn, os.F_OK):
+ cmd.append("--ramdisk_offset")
+ cmd.append(open(fn).read().rstrip("\n"))
+
+ fn = os.path.join(sourcedir, "dt_args")
+ if os.access(fn, os.F_OK):
+ cmd.append("--dt")
+ cmd.append(open(fn).read().rstrip("\n"))
+
+ fn = os.path.join(sourcedir, "pagesize")
+ if os.access(fn, os.F_OK):
+ cmd.append("--pagesize")
+ cmd.append(open(fn).read().rstrip("\n"))
+
+ args = info_dict.get("mkbootimg_args", None)
+ if args and args.strip():
+ cmd.extend(shlex.split(args))
+
+ img_unsigned = None
+ if info_dict.get("vboot", None):
+ img_unsigned = tempfile.NamedTemporaryFile()
+ cmd.extend(["--ramdisk", ramdisk_img.name,
+ "--output", img_unsigned.name])
+ else:
+ cmd.extend(["--ramdisk", ramdisk_img.name,
"--output", img.name])
-
+
p = Run(cmd, stdout=subprocess.PIPE)
p.communicate()
assert p.returncode == 0, "mkbootimg of %s image failed" % (
@@ -453,6 +485,7 @@ def UnzipTemp(filename, pattern=None):
OPTIONS.tempfiles.append(tmp)
def unzip_to_dir(filename, dirname):
+ subprocess.call(["rm", "-rf", dirname + filename, "targetfiles-*"])
cmd = ["unzip", "-o", "-q", filename, "-d", dirname]
if pattern is not None:
cmd.append(pattern)
@@ -581,8 +614,8 @@ def CheckSize(data, target, info_dict):
fs_type = None
limit = None
if info_dict["fstab"]:
- if mount_point == "/userdata":
- mount_point = "/data"
+ if mount_point == "/userdata_extra": mount_point = "/data"
+ if mount_point == "/userdata": mount_point = "/data"
p = info_dict["fstab"][mount_point]
fs_type = p.fs_type
device = p.device
@@ -990,6 +1023,11 @@ class DeviceSpecificParams(object):
used to install the image for the device's baseband processor."""
return self._DoCall("FullOTA_InstallEnd")
+ def FullOTA_PostValidate(self):
+ """Called after installing and validating /system; typically this is
+ used to resize the system partition after a block based installation."""
+ return self._DoCall("FullOTA_PostValidate")
+
def IncrementalOTA_Assertions(self):
"""Called after emitting the block of assertions at the top of an
incremental OTA package. Implementations can add whatever
@@ -1336,7 +1374,10 @@ PARTITION_TYPES = {
"ext4": "EMMC",
"emmc": "EMMC",
"f2fs": "EMMC",
- "squashfs": "EMMC"
+ "squashfs": "EMMC",
+ "ext2": "EMMC",
+ "ext3": "EMMC",
+ "vfat": "EMMC"
}
def GetTypeAndDevice(mount_point, info):
@@ -1399,6 +1440,10 @@ def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
return
sh = """#!/system/bin/sh
+if [ -f /system/etc/recovery-transform.sh ]; then
+ exec sh /system/etc/recovery-transform.sh %(recovery_size)d %(recovery_sha1)s %(boot_size)d %(boot_sha1)s
+fi
+
if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
applypatch %(bonus_args)s %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p && log -t recovery "Installing new recovery image: succeeded" || log -t recovery "Installing new recovery image: failed"
else
diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py
index a52e328..e3425bf 100644
--- a/tools/releasetools/edify_generator.py
+++ b/tools/releasetools/edify_generator.py
@@ -117,20 +117,44 @@ class EdifyGenerator(object):
def AssertDevice(self, device):
"""Assert that the device identifier is the given string."""
- cmd = ('getprop("ro.product.device") == "%s" || '
- 'abort("This package is for \\"%s\\" devices; '
- 'this is a \\"" + getprop("ro.product.device") + "\\".");') % (
- device, device)
+ cmd = ('assert(' +
+ ' || '.join(['getprop("ro.product.device") == "%s" || getprop("ro.build.product") == "%s"'
+ % (i, i) for i in device.split(",")]) +
+ ' || abort("This package is for device: %s; ' +
+ 'this device is " + getprop("ro.product.device") + ".");' +
+ ');') % device
self.script.append(cmd)
def AssertSomeBootloader(self, *bootloaders):
- """Asert that the bootloader version is one of *bootloaders."""
+ """Assert that the bootloader version is one of *bootloaders."""
cmd = ("assert(" +
- " ||\0".join(['getprop("ro.bootloader") == "%s"' % (b,)
+ " || ".join(['getprop("ro.bootloader") == "%s"' % (b,)
for b in bootloaders]) +
+ ' || abort("This package supports bootloader(s): ' +
+ ", ".join(["%s" % (b,) for b in bootloaders]) +
+ '; this device has bootloader " + getprop("ro.bootloader") + ".");' +
");")
self.script.append(self.WordWrap(cmd))
+ def AssertSomeBaseband(self, *basebands):
+ """Assert that the baseband version is one of *basebands."""
+ cmd = ("assert(" +
+ " || ".join(['getprop("ro.baseband") == "%s"' % (b,)
+ for b in basebands]) +
+ ' || abort("This package supports baseband(s): ' +
+ ", ".join(["%s" % (b,) for b in basebands]) +
+ '; this device has baseband " + getprop("ro.baseband") + ".");' +
+ ");")
+ self.script.append(self._WordWrap(cmd))
+
+ def RunBackup(self, command):
+ self.script.append(('run_program("/tmp/install/bin/backuptool.sh", "%s");' % command))
+
+ def ValidateSignatures(self, command):
+ self.script.append('package_extract_file("META-INF/org/cyanogenmod/releasekey", "/tmp/releasekey");')
+ # Exit code 124 == abort. run_program returns raw, so left-shift 8bit
+ self.script.append('run_program("/tmp/install/bin/otasigcheck.sh") != "31744" || abort("Can\'t install this package on top of incompatible data. Please try another package or run a factory reset");')
+
def ShowProgress(self, frac, dur):
"""Update the progress bar, advancing it over 'frac' over the next
'dur' seconds. 'dur' may be zero to advance it via SetProgress
@@ -189,6 +213,12 @@ class EdifyGenerator(object):
p.mount_point, mount_flags))
self.mounts.add(p.mount_point)
+ def Unmount(self, mount_point):
+ """Unmount the partiiton with the given mount_point."""
+ if mount_point in self.mounts:
+ self.mounts.remove(mount_point)
+ self.script.append('unmount("%s");' % (mount_point,))
+
def UnpackPackageDir(self, src, dst):
"""Unpack a given directory from the OTA package into the given
destination directory."""
@@ -310,10 +340,10 @@ class EdifyGenerator(object):
if not self.info.get("use_set_metadata", False):
self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn))
else:
- if capabilities is None:
- capabilities = "0x0"
- cmd = 'set_metadata("%s", "uid", %d, "gid", %d, "mode", 0%o, ' \
- '"capabilities", %s' % (fn, uid, gid, mode, capabilities)
+ cmd = 'set_metadata("%s", "uid", %d, "gid", %d, "mode", 0%o' \
+ % (fn, uid, gid, mode)
+ if capabilities is not None:
+ cmd += ', "capabilities", %s' % ( capabilities )
if selabel is not None:
cmd += ', "selabel", "%s"' % selabel
cmd += ');'
@@ -326,11 +356,11 @@ class EdifyGenerator(object):
self.script.append('set_perm_recursive(%d, %d, 0%o, 0%o, "%s");'
% (uid, gid, dmode, fmode, fn))
else:
- if capabilities is None:
- capabilities = "0x0"
cmd = 'set_metadata_recursive("%s", "uid", %d, "gid", %d, ' \
- '"dmode", 0%o, "fmode", 0%o, "capabilities", %s' \
- % (fn, uid, gid, dmode, fmode, capabilities)
+ '"dmode", 0%o, "fmode", 0%o' \
+ % (fn, uid, gid, dmode, fmode)
+ if capabilities is not None:
+ cmd += ', "capabilities", "%s"' % ( capabilities )
if selabel is not None:
cmd += ', "selabel", "%s"' % selabel
cmd += ');'
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index c486992..e896ba3 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -47,6 +47,31 @@ def CopyInfo(output_zip):
output_zip, os.path.join(OPTIONS.input_tmp, "OTA", "android-info.txt"),
"android-info.txt")
+def AddRadio(output_zip):
+ """If they exist, add RADIO files to the output."""
+ if os.path.isdir(os.path.join(OPTIONS.input_tmp, "RADIO")):
+ for radio_root, radio_dirs, radio_files in os.walk(os.path.join(OPTIONS.input_tmp, "RADIO")):
+ for radio_file in radio_files:
+ output_zip.write(os.path.join(radio_root, radio_file), radio_file)
+
+ # If a filesmap file exists, create a script to flash the radio images based on it
+ filesmap = os.path.join(OPTIONS.input_tmp, "RADIO/filesmap")
+ if os.path.isfile(filesmap):
+ print "creating flash-radio.sh..."
+ filesmap_data = open(filesmap, "r")
+ filesmap_regex = re.compile(r'^(\S+)\s\S+\/by-name\/(\S+).*')
+ tmp_flash_radio = tempfile.NamedTemporaryFile()
+ tmp_flash_radio.write("#!/bin/sh\n\n")
+ for filesmap_line in filesmap_data:
+ filesmap_entry = filesmap_regex.search(filesmap_line)
+ if filesmap_entry:
+ tmp_flash_radio.write("fastboot flash %s %s\n" % (filesmap_entry.group(2), filesmap_entry.group(1)))
+ tmp_flash_radio.flush()
+ if os.path.getsize(tmp_flash_radio.name) > 0:
+ output_zip.write(tmp_flash_radio.name, "flash-radio.sh")
+ else:
+ print "flash-radio.sh is empty, skipping..."
+ tmp_flash_radio.close()
def main(argv):
bootable_only = [False]
@@ -72,6 +97,7 @@ def main(argv):
OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
CopyInfo(output_zip)
+ AddRadio(output_zip)
try:
done = False
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index e5a30ad..77af8ab 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -92,6 +92,18 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package
--stash_threshold <float>
Specifies the threshold that will be used to compute the maximum
allowed stash size (defaults to 0.8).
+
+ --backup <boolean>
+ Enable or disable the execution of backuptool.sh.
+ Disabled by default.
+
+ --override_device <device>
+ Override device-specific asserts. Can be a comma-separated list.
+
+ --override_prop <boolean>
+ Override build.prop items with custom vendor init.
+ Enabled when TARGET_UNIFIED_DEVICE is defined in BoardConfig
+
"""
import sys
@@ -134,6 +146,9 @@ OPTIONS.full_bootloader = False
# Stash size cannot exceed cache_size * threshold.
OPTIONS.cache_size = None
OPTIONS.stash_threshold = 0.8
+OPTIONS.backuptool = False
+OPTIONS.override_device = 'auto'
+OPTIONS.override_prop = False
def MostPopularKey(d, default):
"""Given a dict, return the key corresponding to the largest
@@ -148,7 +163,7 @@ def MostPopularKey(d, default):
def IsSymlink(info):
"""Return true if the zipfile.ZipInfo object passed in represents a
symlink."""
- return (info.external_attr >> 16) == 0o120777
+ return (info.external_attr >> 16) & 0o770000 == 0o120000
def IsRegular(info):
"""Return true if the zipfile.ZipInfo object passed in represents a
@@ -413,7 +428,10 @@ def SignOutput(temp_zip_name, output_zip_name):
def AppendAssertions(script, info_dict, oem_dict=None):
oem_props = info_dict.get("oem_fingerprint_properties")
if oem_props is None or len(oem_props) == 0:
- device = GetBuildProp("ro.product.device", info_dict)
+ if OPTIONS.override_device == "auto":
+ device = GetBuildProp("ro.product.device", info_dict)
+ else:
+ device = OPTIONS.override_device
script.AssertDevice(device)
else:
if oem_dict is None:
@@ -445,8 +463,9 @@ def GetOemProperty(name, oem_props, oem_dict, info_dict):
return oem_dict[name]
return GetBuildProp(name, info_dict)
-
def CalculateFingerprint(oem_props, oem_dict, info_dict):
+ if OPTIONS.override_prop:
+ return GetBuildProp("ro.build.date.utc", info_dict)
if oem_props is None:
return GetBuildProp("ro.build.fingerprint", info_dict)
return "%s/%s/%s:%s" % (
@@ -496,6 +515,15 @@ def GetImage(which, tmpdir, info_dict):
return sparse_img.SparseImage(path, mappath, clobbered_blocks)
+def CopyInstallTools(output_zip):
+ install_path = os.path.join(OPTIONS.input_tmp, "INSTALL")
+ for root, subdirs, files in os.walk(install_path):
+ for f in files:
+ install_source = os.path.join(root, f)
+ install_target = os.path.join("install", os.path.relpath(root, install_path), f)
+ output_zip.write(install_source, install_target)
+
+
def WriteFullOTAPackage(input_zip, output_zip):
# TODO: how to determine this? We don't know what version it will
# be installed on top of. For now, we expect the API just won't
@@ -513,13 +541,18 @@ def WriteFullOTAPackage(input_zip, output_zip):
oem_dict = common.LoadDictionaryFromLines(
open(OPTIONS.oem_source).readlines())
- metadata = {
- "post-build": CalculateFingerprint(oem_props, oem_dict,
- OPTIONS.info_dict),
- "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
- OPTIONS.info_dict),
- "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
- }
+ if OPTIONS.override_prop:
+ metadata = {"post-timestamp": GetBuildProp("ro.build.date.utc",
+ OPTIONS.info_dict),
+ }
+ else:
+ metadata = {"post-build": CalculateFingerprint(
+ oem_props, oem_dict, OPTIONS.info_dict),
+ "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
+ OPTIONS.info_dict),
+ "post-timestamp": GetBuildProp("ro.build.date.utc",
+ OPTIONS.info_dict),
+ }
device_specific = common.DeviceSpecificParams(
input_zip=input_zip,
@@ -533,10 +566,10 @@ def WriteFullOTAPackage(input_zip, output_zip):
has_recovery_patch = HasRecoveryPatch(input_zip)
block_based = OPTIONS.block_based and has_recovery_patch
- if not OPTIONS.omit_prereq:
- ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
- ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
- script.AssertOlderBuild(ts, ts_text)
+ #if not OPTIONS.omit_prereq:
+ # ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
+ # ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
+ # script.AssertOlderBuild(ts, ts_text)
AppendAssertions(script, OPTIONS.info_dict, oem_dict)
device_specific.FullOTA_Assertions()
@@ -585,8 +618,19 @@ else if get_stage("%(bcb_dev)s") == "3/3" then
script.Print("Target: %s" % CalculateFingerprint(
oem_props, oem_dict, OPTIONS.info_dict))
+ script.AppendExtra("ifelse(is_mounted(\"/system\"), unmount(\"/system\"));")
device_specific.FullOTA_InstallBegin()
+ CopyInstallTools(output_zip)
+ script.UnpackPackageDir("install", "/tmp/install")
+ script.SetPermissionsRecursive("/tmp/install", 0, 0, 0755, 0644, None, None)
+ script.SetPermissionsRecursive("/tmp/install/bin", 0, 0, 0755, 0755, None, None)
+
+ if OPTIONS.backuptool:
+ script.Mount("/system")
+ script.RunBackup("backup")
+ script.Unmount("/system")
+
system_progress = 0.75
if OPTIONS.wipe_user_data:
@@ -594,6 +638,14 @@ else if get_stage("%(bcb_dev)s") == "3/3" then
if HasVendorPartition(input_zip):
system_progress -= 0.1
+ script.AppendExtra("if is_mounted(\"/data\") then")
+ script.ValidateSignatures("data")
+ script.AppendExtra("else")
+ script.Mount("/data")
+ script.ValidateSignatures("data")
+ script.Unmount("/data")
+ script.AppendExtra("endif;")
+
if "selinux_fc" in OPTIONS.info_dict:
WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
@@ -658,6 +710,16 @@ else if get_stage("%(bcb_dev)s") == "3/3" then
common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
+ device_specific.FullOTA_PostValidate()
+
+ if OPTIONS.backuptool:
+ script.ShowProgress(0.02, 10)
+ if block_based:
+ script.Mount("/system")
+ script.RunBackup("restore")
+ if block_based:
+ script.Unmount("/system")
+
script.ShowProgress(0.05, 5)
script.WriteRawImage("/boot", "boot.img")
@@ -688,11 +750,15 @@ endif;
script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
WriteMetadata(metadata, output_zip)
+ common.ZipWriteStr(output_zip, "system/build.prop",
+ ""+input_zip.read("SYSTEM/build.prop"))
+
+ common.ZipWriteStr(output_zip, "META-INF/org/cyanogenmod/releasekey",
+ ""+input_zip.read("META/releasekey.txt"))
def WritePolicyConfig(file_name, output_zip):
common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
-
def WriteMetadata(metadata, output_zip):
common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
"".join(["%s=%s\n" % kv
@@ -744,12 +810,16 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
source_version, OPTIONS.target_info_dict,
fstab=OPTIONS.source_info_dict["fstab"])
- metadata = {
- "pre-device": GetBuildProp("ro.product.device",
- OPTIONS.source_info_dict),
- "post-timestamp": GetBuildProp("ro.build.date.utc",
- OPTIONS.target_info_dict),
- }
+ if OPTIONS.override_prop:
+ metadata = {"post-timestamp": GetBuildProp("ro.build.date.utc",
+ OPTIONS.target_info_dict),
+ }
+ else:
+ metadata = {"pre-device": GetBuildProp("ro.product.device",
+ OPTIONS.source_info_dict),
+ "post-timestamp": GetBuildProp("ro.build.date.utc",
+ OPTIONS.target_info_dict),
+ }
device_specific = common.DeviceSpecificParams(
source_zip=source_zip,
@@ -767,10 +837,11 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
# devices with thumbprints are all using file-based OTAs. Long term we
# should factor out the common parts into a shared one to avoid further
# divergence.
- source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
- target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
- metadata["pre-build"] = source_fp
- metadata["post-build"] = target_fp
+ if not OPTIONS.override_prop:
+ source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
+ target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
+ metadata["pre-build"] = source_fp
+ metadata["post-build"] = target_fp
source_boot = common.GetBootableImage(
"/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
@@ -1144,12 +1215,16 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
oem_dict = common.LoadDictionaryFromLines(
open(OPTIONS.oem_source).readlines())
- metadata = {
- "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
- OPTIONS.source_info_dict),
- "post-timestamp": GetBuildProp("ro.build.date.utc",
- OPTIONS.target_info_dict),
- }
+ if OPTIONS.override_prop:
+ metadata = {"post-timestamp": GetBuildProp("ro.build.date.utc",
+ OPTIONS.target_info_dict),
+ }
+ else:
+ metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
+ OPTIONS.source_info_dict),
+ "post-timestamp": GetBuildProp("ro.build.date.utc",
+ OPTIONS.target_info_dict),
+ }
device_specific = common.DeviceSpecificParams(
source_zip=source_zip,
@@ -1169,20 +1244,19 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
else:
vendor_diff = None
- target_fp = CalculateFingerprint(oem_props, oem_dict,
- OPTIONS.target_info_dict)
- source_fp = CalculateFingerprint(oem_props, oem_dict,
- OPTIONS.source_info_dict)
+ if not OPTIONS.override_prop:
+ target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
+ source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
- if oem_props is None:
- script.AssertSomeFingerprint(source_fp, target_fp)
- else:
- script.AssertSomeThumbprint(
- GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
- GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
+ if oem_props is None:
+ script.AssertSomeFingerprint(source_fp, target_fp)
+ else:
+ script.AssertSomeThumbprint(
+ GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
+ GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
- metadata["pre-build"] = source_fp
- metadata["post-build"] = target_fp
+ metadata["pre-build"] = source_fp
+ metadata["post-build"] = target_fp
source_boot = common.GetBootableImage(
"/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
@@ -1499,7 +1573,6 @@ endif;
WriteMetadata(metadata, output_zip)
-
def main(argv):
def option_handler(o, a):
@@ -1550,6 +1623,12 @@ def main(argv):
except ValueError:
raise ValueError("Cannot parse value %r for option %r - expecting "
"a float" % (a, o))
+ elif o in ("--backup"):
+ OPTIONS.backuptool = bool(a.lower() == 'true')
+ elif o in ("--override_device"):
+ OPTIONS.override_device = a
+ elif o in ("--override_prop"):
+ OPTIONS.override_prop = bool(a.lower() == 'true')
else:
return False
return True
@@ -1575,6 +1654,9 @@ def main(argv):
"verify",
"no_fallback_to_full",
"stash_threshold=",
+ "backup=",
+ "override_device=",
+ "override_prop="
], extra_option_handler=option_handler)
if len(args) != 2:
diff --git a/tools/repopick.py b/tools/repopick.py
new file mode 100755
index 0000000..ac8875a
--- /dev/null
+++ b/tools/repopick.py
@@ -0,0 +1,355 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2013-15 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
+from xml.etree import ElementTree
+
+try:
+ # For python3
+ import urllib.error
+ import urllib.request
+except ImportError:
+ # For python2
+ import imp
+ import urllib2
+ urllib = imp.new_module('urllib')
+ urllib.error = urllib2
+ urllib.request = urllib2
+
+
+# Verifies whether pathA is a subdirectory (or the same) as pathB
+def is_subdir(a, b):
+ a = os.path.realpath(a) + '/'
+ b = os.path.realpath(b) + '/'
+ return b == a[:len(b)]
+
+
+def fetch_query_via_ssh(remote_url, query):
+ """Given a remote_url and a query, return the list of changes that fit it
+ This function is slightly messy - the ssh api does not return data in the same structure as the HTTP REST API
+ We have to get the data, then transform it to match what we're expecting from the HTTP RESET API"""
+ if remote_url.count(':') == 2:
+ (uri, userhost, port) = remote_url.split(':')
+ userhost = userhost[2:]
+ elif remote_url.count(':') == 1:
+ (uri, userhost) = remote_url.split(':')
+ userhost = userhost[2:]
+ port = 29418
+ else:
+ raise Exception('Malformed URI: Expecting ssh://[user@]host[:port]')
+
+
+ out = subprocess.check_output(['ssh', '-x', '-p{0}'.format(port), userhost, 'gerrit', 'query', '--format=JSON --patch-sets --current-patch-set', query])
+
+ reviews = []
+ for line in out.split('\n'):
+ try:
+ data = json.loads(line)
+ # make our data look like the http rest api data
+ review = {
+ 'branch': data['branch'],
+ 'change_id': data['id'],
+ 'current_revision': data['currentPatchSet']['revision'],
+ 'number': int(data['number']),
+ 'revisions': {patch_set['revision']: {
+ 'number': int(patch_set['number']),
+ 'fetch': {
+ 'ssh': {
+ 'ref': patch_set['ref'],
+ 'url': u'ssh://{0}:{1}/{2}'.format(userhost, port, data['project'])
+ }
+ }
+ } for patch_set in data['patchSets']},
+ 'subject': data['subject'],
+ 'project': data['project'],
+ 'status': data['status']
+ }
+ reviews.append(review)
+ except:
+ pass
+ args.quiet or print('Found {0} reviews'.format(len(reviews)))
+ return reviews
+
+
+def fetch_query_via_http(remote_url, query):
+
+ """Given a query, fetch the change numbers via http"""
+ url = '{0}/changes/?q={1}&o=CURRENT_REVISION&o=ALL_REVISIONS'.format(remote_url, query)
+ data = urllib.request.urlopen(url).read().decode('utf-8')
+ reviews = json.loads(data[5:])
+
+ for review in reviews:
+ review[u'number'] = review.pop('_number')
+
+ return reviews
+
+
+def fetch_query(remote_url, query):
+ """Wrapper for fetch_query_via_proto functions"""
+ if remote_url[0:3] == 'ssh':
+ return fetch_query_via_ssh(remote_url, query)
+ elif remote_url[0:4] == 'http':
+ return fetch_query_via_http(remote_url, query.replace(' ', '+'))
+ else:
+ raise Exception('Gerrit URL should be in the form http[s]://hostname/ or ssh://[user@]host[:port]')
+
+if __name__ == '__main__':
+ # Default to CyanogenMod Gerrit
+ default_gerrit = 'http://review.cyanogenmod.org'
+
+ 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 (or any gerrit instance of your choosing)
+
+ 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 conjunction 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. Use {change number}/{patchset number} to get a specific revision.')
+ 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')
+ parser.add_argument('-f', '--force', action='store_true', help='force cherry pick even if change is closed')
+ parser.add_argument('-p', '--pull', action='store_true', help='execute pull instead of cherry-pick')
+ parser.add_argument('-P', '--path', help='use the specified path for the change')
+ parser.add_argument('-t', '--topic', help='pick all commits from a specified topic')
+ parser.add_argument('-Q', '--query', help='pick all commits using the specified query')
+ parser.add_argument('-g', '--gerrit', default=default_gerrit, help='Gerrit Instance to use. Form proto://[user@]host[:port]')
+ args = parser.parse_args()
+ if not args.start_branch 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')
+
+ if (1 << bool(args.change_number) << bool(args.topic) << bool(args.query)) != 2:
+ parser.error('One (and only one) of change_number, topic, and query are allowed')
+
+ # 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_subdir(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.check_output(['repo', 'info'])
+ needs_abandon = False
+ for pline in plist:
+ matchObj = re.match(r'Local Branches.*\[(.*)\]', pline)
+ 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
+
+ if needs_abandon:
+ # Perform the abandon only if the branch already exists
+ if not args.quiet:
+ print('Abandoning branch: %s' % args.start_branch[0])
+ subprocess.check_output(['repo', 'abandon', args.start_branch[0]])
+ if not args.quiet:
+ print('')
+
+ # Get the master manifest from repo
+ # - convert project name and revision to a path
+ project_name_to_data = {}
+ manifest = subprocess.check_output(['repo', 'manifest'])
+ xml_root = ElementTree.fromstring(manifest)
+ projects = xml_root.findall('project')
+ default_revision = xml_root.findall('default')[0].get('revision').split('/')[-1]
+
+ #dump project data into the a list of dicts with the following data:
+ #{project: {path, revision}}
+
+ for project in projects:
+ name = project.get('name')
+ path = project.get('path')
+ revision = project.get('revision')
+ if revision is None:
+ revision = default_revision
+
+ if not name in project_name_to_data:
+ project_name_to_data[name] = {}
+ project_name_to_data[name][revision] = path
+
+ # get data on requested changes
+ reviews = []
+ change_numbers = []
+ if args.topic:
+ reviews = fetch_query(args.gerrit, 'topic:{0}'.format(args.topic))
+ change_numbers = sorted([str(r['number']) for r in reviews])
+ if args.query:
+ reviews = fetch_query(args.gerrit, args.query)
+ change_numbers = sorted([str(r['number']) for r in reviews])
+ if args.change_number:
+ reviews = fetch_query(args.gerrit, ' OR '.join('change:{0}'.format(x.split('/')[0]) for x in args.change_number))
+ change_numbers = args.change_number
+
+ # make list of things to actually merge
+ mergables = []
+
+ for change in change_numbers:
+ patchset = None
+ if '/' in change:
+ (change, patchset) = change.split('/')
+ change = int(change)
+
+ review = [x for x in reviews if x['number'] == change][0]
+ mergables.append({
+ 'subject': review['subject'],
+ 'project': review['project'],
+ 'branch': review['branch'],
+ 'change_number': review['number'],
+ 'status': review['status'],
+ 'fetch': None
+ })
+ mergables[-1]['fetch'] = review['revisions'][review['current_revision']]['fetch']
+ mergables[-1]['id'] = change
+ if patchset:
+ try:
+ mergables[-1]['fetch'] = [x['fetch'] for x in review['revisions'] if x['_number'] == patchset][0]
+ mergables[-1]['id'] = '{0}/{1}'.format(change, patchset)
+ except (IndexError, ValueError):
+ args.quiet or print('ERROR: The patch set {0}/{1} could not be found, using CURRENT_REVISION instead.'.format(change, patchset))
+
+ for item in mergables:
+ args.quiet or print('Applying change number {0}...'.format(item['id']))
+ # Check if change is open and exit if it's not, unless -f is specified
+ if (item['status'] != 'OPEN' and item['status'] != 'NEW') and not args.query:
+ if args.force:
+ print('!! Force-picking a closed change !!\n')
+ else:
+ print('Change status is ' + item['status'] + '. Skipping the cherry pick.\nUse -f to force this pick.')
+ continue
+
+ # Convert the project name to a project path
+ # - check that the project path exists
+ project_path = None
+
+ if item['project'] in project_name_to_data and item['branch'] in project_name_to_data[item['project']]:
+ project_path = project_name_to_data[item['project']][item['branch']]
+ elif args.path:
+ project_path = args.path
+ elif args.ignore_missing:
+ print('WARNING: Skipping {0} since there is no project directory for: {1}\n'.format(item['id'], item['project']))
+ continue
+ else:
+ sys.stderr.write('ERROR: For {0}, could not determine the project path for project {1}\n'.format(item['id'], item['project']))
+ 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:
+ subprocess.check_output(['repo', 'start', args.start_branch[0], project_path])
+
+ # Print out some useful info
+ if not args.quiet:
+ print('--> Subject: "{0}"'.format(item['subject']))
+ print('--> Project path: {0}'.format(project_path))
+ print('--> Change number: {0} (Patch Set {0})'.format(item['id']))
+
+ if 'anonymous http' in item['fetch']:
+ method = 'anonymous http'
+ else:
+ method = 'ssh'
+
+ # Try fetching from GitHub first if using default gerrit
+ if args.gerrit == default_gerrit:
+ if args.verbose:
+ print('Trying to fetch the change from GitHub')
+
+ if args.pull:
+ cmd = ['git pull --no-edit github', item['fetch'][method]['ref']]
+ else:
+ cmd = ['git fetch github', item['fetch'][method]['ref']]
+ if args.quiet:
+ cmd.append('--quiet')
+ else:
+ print(cmd)
+ result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True)
+ if result != 0:
+ print('ERROR: git command failed')
+ sys.exit(result)
+ FETCH_HEAD = '{0}/.git/FETCH_HEAD'.format(project_path)
+ # Check if it worked
+ if args.gerrit != default_gerrit or os.stat(FETCH_HEAD).st_size == 0:
+ # If not using the default gerrit or github failed, fetch from gerrit.
+ if args.verbose:
+ if args.gerrit == default_gerrit:
+ print('Fetching from GitHub didn\'t work, trying to fetch the change from Gerrit')
+ else:
+ print('Fetching from {0}'.format(args.gerrit))
+
+ if args.pull:
+ cmd = ['git pull --no-edit', item['fetch'][method]['url'], item['fetch'][method]['ref']]
+ else:
+ cmd = ['git fetch', item['fetch'][method]['url'], item['fetch'][method]['ref']]
+ if args.quiet:
+ cmd.append('--quiet')
+ else:
+ print(cmd)
+ result = subprocess.call([' '.join(cmd)], cwd=project_path, shell=True)
+ if result != 0:
+ print('ERROR: git command failed')
+ sys.exit(result)
+ # Perform the cherry-pick
+ if not args.pull:
+ cmd = ['git cherry-pick FETCH_HEAD']
+ if args.quiet:
+ cmd_out = open(os.devnull, 'wb')
+ else:
+ cmd_out = None
+ result = subprocess.call(cmd, cwd=project_path, shell=True, stdout=cmd_out, stderr=cmd_out)
+ if result != 0:
+ print('ERROR: git command failed')
+ sys.exit(result)
+ if not args.quiet:
+ print('')
diff --git a/tools/roomservice.py b/tools/roomservice.py
new file mode 100755
index 0000000..01a7154
--- /dev/null
+++ b/tools/roomservice.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+# Copyright (C) 2012-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.
+
+from __future__ import print_function
+
+import base64
+import json
+import netrc
+import os
+import re
+import sys
+try:
+ # For python3
+ import urllib.error
+ import urllib.parse
+ import urllib.request
+except ImportError:
+ # For python2
+ import imp
+ import urllib2
+ import urlparse
+ urllib = imp.new_module('urllib')
+ urllib.error = urllib2
+ urllib.parse = urlparse
+ urllib.request = urllib2
+
+from xml.etree import ElementTree
+
+product = sys.argv[1];
+
+if len(sys.argv) > 2:
+ depsonly = sys.argv[2]
+else:
+ depsonly = None
+
+try:
+ device = product[product.index("_") + 1:]
+except:
+ device = product
+
+if not depsonly:
+ print("Device %s not found. Attempting to retrieve device repository from CyanogenMod Github (http://github.com/CyanogenMod)." % device)
+
+repositories = []
+
+try:
+ authtuple = netrc.netrc().authenticators("api.github.com")
+
+ if authtuple:
+ githubauth = base64.encodestring('%s:%s' % (authtuple[0], authtuple[2])).replace('\n', '')
+ else:
+ githubauth = None
+except:
+ githubauth = None
+
+def add_auth(githubreq):
+ if githubauth:
+ githubreq.add_header("Authorization","Basic %s" % githubauth)
+
+if not depsonly:
+ githubreq = urllib.request.Request("https://api.github.com/search/repositories?q=%s+user:CyanogenMod+in:name+fork:true" % device)
+ add_auth(githubreq)
+ try:
+ result = json.loads(urllib.request.urlopen(githubreq).read().decode())
+ except urllib.error.URLError:
+ print("Failed to search GitHub")
+ sys.exit()
+ except ValueError:
+ print("Failed to parse return data from GitHub")
+ sys.exit()
+ for res in result.get('items', []):
+ repositories.append(res)
+
+local_manifests = r'.repo/local_manifests'
+if not os.path.exists(local_manifests): os.makedirs(local_manifests)
+
+def exists_in_tree(lm, repository):
+ for child in lm.getchildren():
+ if child.attrib['name'].endswith(repository):
+ return True
+ return False
+
+# in-place prettyprint formatter
+def indent(elem, level=0):
+ i = "\n" + level*" "
+ if len(elem):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + " "
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ for elem in elem:
+ indent(elem, level+1)
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ else:
+ if level and (not elem.tail or not elem.tail.strip()):
+ elem.tail = i
+
+def get_default_revision():
+ m = ElementTree.parse(".repo/manifest.xml")
+ d = m.findall('default')[0]
+ r = d.get('revision')
+ return r.replace('refs/heads/', '').replace('refs/tags/', '')
+
+def get_from_manifest(devicename):
+ try:
+ lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
+ lm = lm.getroot()
+ except:
+ lm = ElementTree.Element("manifest")
+
+ for localpath in lm.findall("project"):
+ if re.search("android_device_.*_%s$" % device, localpath.get("name")):
+ return localpath.get("path")
+
+ # Devices originally from AOSP are in the main manifest...
+ try:
+ mm = ElementTree.parse(".repo/manifest.xml")
+ mm = mm.getroot()
+ except:
+ mm = ElementTree.Element("manifest")
+
+ for localpath in mm.findall("project"):
+ if re.search("android_device_.*_%s$" % device, localpath.get("name")):
+ return localpath.get("path")
+
+ return None
+
+def is_in_manifest(projectname):
+ try:
+ lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
+ lm = lm.getroot()
+ except:
+ lm = ElementTree.Element("manifest")
+
+ for localpath in lm.findall("project"):
+ if localpath.get("name") == projectname:
+ return 1
+
+ ## Search in main manifest, too
+ try:
+ lm = ElementTree.parse(".repo/manifest.xml")
+ lm = lm.getroot()
+ except:
+ lm = ElementTree.Element("manifest")
+
+ for localpath in lm.findall("project"):
+ if localpath.get("name") == projectname:
+ return 1
+
+ return None
+
+def add_to_manifest(repositories, fallback_branch = None):
+ try:
+ lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
+ lm = lm.getroot()
+ except:
+ lm = ElementTree.Element("manifest")
+
+ for repository in repositories:
+ repo_name = repository['repository']
+ repo_target = repository['target_path']
+ if exists_in_tree(lm, repo_name):
+ print('CyanogenMod/%s already exists' % (repo_name))
+ continue
+
+ print('Adding dependency: CyanogenMod/%s -> %s' % (repo_name, repo_target))
+ project = ElementTree.Element("project", attrib = { "path": repo_target,
+ "remote": "github", "name": "CyanogenMod/%s" % repo_name })
+
+ if 'branch' in repository:
+ project.set('revision',repository['branch'])
+ elif fallback_branch:
+ print("Using fallback branch %s for %s" % (fallback_branch, repo_name))
+ project.set('revision', fallback_branch)
+ else:
+ print("Using default branch for %s" % repo_name)
+
+ lm.append(project)
+
+ indent(lm, 0)
+ raw_xml = ElementTree.tostring(lm).decode()
+ raw_xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + raw_xml
+
+ f = open('.repo/local_manifests/roomservice.xml', 'w')
+ f.write(raw_xml)
+ f.close()
+
+def fetch_dependencies(repo_path, fallback_branch = None):
+ print('Looking for dependencies')
+ dependencies_path = repo_path + '/cm.dependencies'
+ syncable_repos = []
+
+ if os.path.exists(dependencies_path):
+ dependencies_file = open(dependencies_path, 'r')
+ dependencies = json.loads(dependencies_file.read())
+ fetch_list = []
+
+ for dependency in dependencies:
+ if not is_in_manifest("CyanogenMod/%s" % dependency['repository']):
+ fetch_list.append(dependency)
+ syncable_repos.append(dependency['target_path'])
+
+ dependencies_file.close()
+
+ if len(fetch_list) > 0:
+ print('Adding dependencies to manifest')
+ add_to_manifest(fetch_list, fallback_branch)
+ else:
+ print('Dependencies file not found, bailing out.')
+
+ if len(syncable_repos) > 0:
+ print('Syncing dependencies')
+ os.system('repo sync --force-sync %s' % ' '.join(syncable_repos))
+
+ for deprepo in syncable_repos:
+ fetch_dependencies(deprepo)
+
+def has_branch(branches, revision):
+ return revision in [branch['name'] for branch in branches]
+
+if depsonly:
+ repo_path = get_from_manifest(device)
+ if repo_path:
+ fetch_dependencies(repo_path)
+ else:
+ print("Trying dependencies-only mode on a non-existing device tree?")
+
+ sys.exit()
+
+else:
+ for repository in repositories:
+ repo_name = repository['name']
+ if repo_name.startswith("android_device_") and repo_name.endswith("_" + device):
+ print("Found repository: %s" % repository['name'])
+
+ manufacturer = repo_name.replace("android_device_", "").replace("_" + device, "")
+
+ default_revision = get_default_revision()
+ print("Default revision: %s" % default_revision)
+ print("Checking branch info")
+ githubreq = urllib.request.Request(repository['branches_url'].replace('{/branch}', ''))
+ add_auth(githubreq)
+ result = json.loads(urllib.request.urlopen(githubreq).read().decode())
+
+ ## Try tags, too, since that's what releases use
+ if not has_branch(result, default_revision):
+ githubreq = urllib.request.Request(repository['tags_url'].replace('{/tag}', ''))
+ add_auth(githubreq)
+ result.extend (json.loads(urllib.request.urlopen(githubreq).read().decode()))
+
+ repo_path = "device/%s/%s" % (manufacturer, device)
+ adding = {'repository':repo_name,'target_path':repo_path}
+
+ fallback_branch = None
+ if not has_branch(result, default_revision):
+ if os.getenv('ROOMSERVICE_BRANCHES'):
+ fallbacks = list(filter(bool, os.getenv('ROOMSERVICE_BRANCHES').split(' ')))
+ for fallback in fallbacks:
+ if has_branch(result, fallback):
+ print("Using fallback branch: %s" % fallback)
+ fallback_branch = fallback
+ break
+
+ if not fallback_branch:
+ print("Default revision %s not found in %s. Bailing." % (default_revision, repo_name))
+ print("Branches found:")
+ for branch in [branch['name'] for branch in result]:
+ print(branch)
+ print("Use the ROOMSERVICE_BRANCHES environment variable to specify a list of fallback branches.")
+ sys.exit()
+
+ add_to_manifest([adding], fallback_branch)
+
+ print("Syncing repository to retrieve project.")
+ os.system('repo sync --force-sync %s' % repo_path)
+ print("Repository synced!")
+
+ fetch_dependencies(repo_path, fallback_branch)
+ print("Done")
+ sys.exit()
+
+print("Repository for %s not found in the CyanogenMod Github repository list. If this is in error, you may need to manually add it to your local_manifests/roomservice.xml." % device)