diff options
Diffstat (limited to 'tools')
42 files changed, 1812 insertions, 486 deletions
@@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function + import os import os.path import re 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/check_radio_versions.py b/tools/check_radio_versions.py index ebe621f..2617424 100755 --- a/tools/check_radio_versions.py +++ b/tools/check_radio_versions.py @@ -14,8 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function + import sys -import os try: from hashlib import sha1 @@ -52,8 +53,9 @@ for item in sys.argv[2:]: try: f = open(fn + ".sha1") except IOError: - if not bad: print - print "*** Error opening \"%s.sha1\"; can't verify %s" % (fn, key) + if not bad: + print() + print("*** Error opening \"%s.sha1\"; can't verify %s" % (fn, key)) bad = True continue for line in f: @@ -63,17 +65,19 @@ for item in sys.argv[2:]: versions[h] = v if digest not in versions: - if not bad: print - print "*** SHA-1 hash of \"%s\" doesn't appear in \"%s.sha1\"" % (fn, fn) + if not bad: + print() + print("*** SHA-1 hash of \"%s\" doesn't appear in \"%s.sha1\"" % (fn, fn)) bad = True continue if versions[digest] not in values: - if not bad: print - print "*** \"%s\" is version %s; not any %s allowed by \"%s\"." % ( - fn, versions[digest], key, sys.argv[1]) + if not bad: + print() + print("*** \"%s\" is version %s; not any %s allowed by \"%s\"." % ( + fn, versions[digest], key, sys.argv[1])) bad = True if bad: - print + print() sys.exit(1) diff --git a/tools/compare_fileslist.py b/tools/compare_fileslist.py index 1f507d8..64ad3ae 100755 --- a/tools/compare_fileslist.py +++ b/tools/compare_fileslist.py @@ -15,7 +15,16 @@ # limitations under the License. # -import cgi, os, string, sys +from __future__ import print_function + +import cgi, os, sys + + +def iteritems(obj): + if hasattr(obj, 'iteritems'): + return obj.iteritems() + return obj.items() + def IsDifferent(row): val = None @@ -33,27 +42,27 @@ def main(argv): data = {} index = 0 for input in inputs: - f = file(input, "r") + f = open(input) lines = f.readlines() f.close() - lines = map(string.split, lines) - lines = map(lambda (x,y): (y,int(x)), lines) + lines = [l.strip() for l in lines] + lines = [(x_y[1],int(x_y[0])) for x_y in lines] for fn,sz in lines: - if not data.has_key(fn): + if fn not in data: data[fn] = {} data[fn][index] = sz index = index + 1 rows = [] - for fn,sizes in data.iteritems(): + for fn,sizes in iteritems(data): row = [fn] for i in range(0,index): - if sizes.has_key(i): + if i in sizes: row.append(sizes[i]) else: row.append(None) rows.append(row) rows = sorted(rows, key=lambda x: x[0]) - print """<html> + print("""<html> <head> <style type="text/css"> .fn, .sz, .z, .d { @@ -78,27 +87,27 @@ def main(argv): </style> </head> <body> - """ - print "<table>" - print "<tr>" + """) + print("<table>") + print("<tr>") for input in inputs: combo = input.split(os.path.sep)[1] - print " <td class='fn'>%s</td>" % cgi.escape(combo) - print "</tr>" + print(" <td class='fn'>%s</td>" % cgi.escape(combo)) + print("</tr>") for row in rows: - print "<tr>" + print("<tr>") for sz in row[1:]: if not sz: - print " <td class='z'> </td>" + print(" <td class='z'> </td>") elif IsDifferent(row[1:]): - print " <td class='d'>%d</td>" % sz + print(" <td class='d'>%d</td>" % sz) else: - print " <td class='sz'>%d</td>" % sz - print " <td class='fn'>%s</td>" % cgi.escape(row[0]) - print "</tr>" - print "</table>" - print "</body></html>" + print(" <td class='sz'>%d</td>" % sz) + print(" <td class='fn'>%s</td>" % cgi.escape(row[0])) + print("</tr>") + print("</table>") + print("</body></html>") if __name__ == '__main__': main(sys.argv) 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/mkvendor.sh b/tools/device/mkvendor.sh new file mode 100755 index 0000000..18671c0 --- /dev/null +++ b/tools/device/mkvendor.sh @@ -0,0 +1,113 @@ +#!/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 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/diff_package_overlays.py b/tools/diff_package_overlays.py index 0e2c773..687e1d0 100755 --- a/tools/diff_package_overlays.py +++ b/tools/diff_package_overlays.py @@ -34,11 +34,13 @@ Format of current_overlays.txt and previous_overlays.txt: ... """ +from __future__ import print_function + import sys def main(argv): if len(argv) != 4: - print >> sys.stderr, __doc__ + print(sys.stderr, __doc__) sys.exit(1) f = open(argv[1]) @@ -85,7 +87,7 @@ def main(argv): # Print out the package names that have overlay change. for r in result: - print r + print(r) if __name__ == "__main__": main(sys.argv) diff --git a/tools/event_log_tags.py b/tools/event_log_tags.py index 645839e..93244a4 100644 --- a/tools/event_log_tags.py +++ b/tools/event_log_tags.py @@ -14,6 +14,8 @@ """A module for reading and parsing event-log-tags files.""" +from __future__ import print_function + import re import sys @@ -55,7 +57,7 @@ class TagFile(object): if file_object is None: try: file_object = open(filename, "rb") - except (IOError, OSError), e: + except (IOError, OSError) as e: self.AddError(str(e)) return @@ -100,7 +102,7 @@ class TagFile(object): self.tags.append(Tag(tag, tagname, description, self.filename, self.linenum)) - except (IOError, OSError), e: + except (IOError, OSError) as e: self.AddError(str(e)) @@ -130,6 +132,6 @@ def WriteOutput(output_file, data): out = open(output_file, "wb") out.write(data) out.close() - except (IOError, OSError), e: - print >> sys.stderr, "failed to write %s: %s" % (output_file, e) + except (IOError, OSError) as e: + print("failed to write %s: %s" % (output_file, e), file=sys.stderr) sys.exit(1) diff --git a/tools/fileslist.py b/tools/fileslist.py index a11efaa..1538a30 100755 --- a/tools/fileslist.py +++ b/tools/fileslist.py @@ -15,11 +15,13 @@ # limitations under the License. # +from __future__ import print_function + import operator, os, sys def get_file_size(path): st = os.lstat(path) - return st.st_size; + return st.st_size def main(argv): output = [] @@ -39,7 +41,7 @@ def main(argv): pass output.sort(key=operator.itemgetter(0), reverse=True) for row in output: - print "%12d %s" % row + print("%12d %s" % row) if __name__ == '__main__': main(sys.argv) diff --git a/tools/filter-product-graph.py b/tools/filter-product-graph.py index b3a5b42..d6100d8 100755 --- a/tools/filter-product-graph.py +++ b/tools/filter-product-graph.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # vim: ts=2 sw=2 nocindent +from __future__ import print_function + import re import sys @@ -55,13 +57,13 @@ def main(): deps = [dep for dep in deps if dep[1] in included] infos = [info for info in infos if info[0] in included] - print "digraph {" - print "graph [ ratio=.5 ];" + print("digraph {") + print("graph [ ratio=.5 ];") for dep in deps: - print '"%s" -> "%s"' % dep + print('"%s" -> "%s"' % dep) for info in infos: - print '"%s"%s' % info - print "}" + print('"%s"%s' % info) + print("}") if __name__ == "__main__": diff --git a/tools/findleaves.py b/tools/findleaves.py index 3a9e508..d97ed74 100755 --- a/tools/findleaves.py +++ b/tools/findleaves.py @@ -20,12 +20,14 @@ # the search in a given subdirectory when the file is found. # +from __future__ import print_function + import os import sys def perform_find(mindepth, prune, dirlist, filename): result = [] - pruneleaves = set(map(lambda x: os.path.split(x)[1], prune)) + pruneleaves = set([os.path.split(x)[1] for x in prune]) for rootdir in dirlist: rootdepth = rootdir.count("/") for root, dirs, files in os.walk(rootdir, followlinks=True): @@ -92,7 +94,7 @@ def main(argv): results = list(set(perform_find(mindepth, prune, dirlist, filename))) results.sort() for r in results: - print r + print(r) if __name__ == "__main__": main(sys.argv) diff --git a/tools/generate-notice-files.py b/tools/generate-notice-files.py index 4571b70..36630db 100755 --- a/tools/generate-notice-files.py +++ b/tools/generate-notice-files.py @@ -20,6 +20,9 @@ Generate the Android notice files, including both text and html files. -h to display this usage message and exit. """ + +from __future__ import print_function + from collections import defaultdict import getopt import hashlib @@ -40,22 +43,22 @@ HTML_ESCAPE_TABLE = { try: opts, args = getopt.getopt(sys.argv[1:], "h") -except getopt.GetoptError, err: - print str(err) - print __doc__ +except getopt.GetoptError as err: + print(str(err)) + print(__doc__) sys.exit(2) for o, a in opts: if o == "-h": - print __doc__ + print(__doc__) sys.exit(2) else: - print >> sys.stderr, "unhandled option %s" % (o,) + print("unhandled option %s" % o) if len(args) != 4: - print """need exactly four arguments, the two output files, the file title - and the directory containing notices, not %d""" % (len(args),) - print __doc__ + print("""need exactly four arguments, the two output files, the file title + and the directory containing notices, not %d""" % len(args)) + print(__doc__) sys.exit(1) def hexify(s): @@ -107,13 +110,13 @@ def combine_notice_files_html(file_hash, input_dir, output_filename): # Open the output file, and output the header pieces output_file = open(output_filename, "wb") - print >> output_file, "<html><head>" - print >> output_file, HTML_OUTPUT_CSS - print >> output_file, '</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">' + print(output_file, "<html><head>", file=output_file) + print(HTML_OUTPUT_CSS, file=output_file) + print('</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">', file=output_file) # Output our table of contents - print >> output_file, '<div class="toc">' - print >> output_file, "<ul>" + print('<div class="toc">', file=output_file) + print("<ul>", file=output_file) # Flatten the list of lists into a single list of filenames sorted_filenames = sorted(itertools.chain.from_iterable(file_hash.values())) @@ -121,31 +124,31 @@ def combine_notice_files_html(file_hash, input_dir, output_filename): # Print out a nice table of contents for filename in sorted_filenames: stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename) - print >> output_file, '<li><a href="#id%d">%s</a></li>' % (id_table.get(filename), stripped_filename) + print('<li><a href="#id%d">%s</a></li>' % (id_table.get(filename), stripped_filename), file=output_file) - print >> output_file, "</ul>" - print >> output_file, "</div><!-- table of contents -->" + print("</ul>", file=output_file) + print("</div><!-- table of contents -->", file=output_file) # Output the individual notice file lists - print >>output_file, '<table cellpadding="0" cellspacing="0" border="0">' + print('<table cellpadding="0" cellspacing="0" border="0">', file=output_file) for value in file_hash.values(): - print >> output_file, '<tr id="id%d"><td class="same-license">' % id_table.get(value[0]) - print >> output_file, '<div class="label">Notices for file(s):</div>' - print >> output_file, '<div class="file-list">' + print('<tr id="id%d"><td class="same-license">' % id_table.get(value[0]), file=output_file) + print('<div class="label">Notices for file(s):</div>', file=output_file) + print('<div class="file-list">', file=output_file) for filename in sorted(value): - print >> output_file, "%s <br/>" % (SRC_DIR_STRIP_RE.sub(r"\1", filename)) - print >> output_file, "</div><!-- file-list -->" - print >> output_file - print >> output_file, '<pre class="license-text">' - print >> output_file, html_escape(open(value[0]).read()) - print >> output_file, "</pre><!-- license-text -->" - print >> output_file, "</td></tr><!-- same-license -->" - print >> output_file - print >> output_file - print >> output_file + print("%s <br/>" % (SRC_DIR_STRIP_RE.sub(r"\1", filename)), file=output_file) + print("</div><!-- file-list -->", file=output_file) + print(file=output_file) + print('<pre class="license-text">', file=output_file) + print(html_escape(open(value[0]).read()), file=output_file) + print("</pre><!-- license-text -->", file=output_file) + print("</td></tr><!-- same-license -->", file=output_file) + print(file=output_file) + print(file=output_file) + print(file=output_file) # Finish off the file output - print >> output_file, "</table>" - print >> output_file, "</body></html>" + print( "</table>", file=output_file) + print("</body></html>", file=output_file) output_file.close() def combine_notice_files_text(file_hash, input_dir, output_filename, file_title): @@ -153,14 +156,14 @@ def combine_notice_files_text(file_hash, input_dir, output_filename, file_title) SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt") output_file = open(output_filename, "wb") - print >> output_file, file_title + print(file_title, file=output_file) for value in file_hash.values(): - print >> output_file, "============================================================" - print >> output_file, "Notices for file(s):" + print("============================================================", file=output_file) + print("Notices for file(s):", file=output_file) for filename in sorted(value): - print >> output_file, SRC_DIR_STRIP_RE.sub(r"\1", filename) - print >> output_file, "------------------------------------------------------------" - print >> output_file, open(value[0]).read() + print(SRC_DIR_STRIP_RE.sub(r"\1", filename), file=output_file) + print("------------------------------------------------------------", file=output_file) + print(open(value[0]).read(), file=output_file) output_file.close() def main(args): @@ -179,9 +182,9 @@ def main(args): files_with_same_hash[file_md5sum].append(filename) - print "Combining NOTICE files into HTML" + print("Combining NOTICE files into HTML") combine_notice_files_html(files_with_same_hash, input_dir, html_output_file) - print "Combining NOTICE files into text" + print("Combining NOTICE files into text") combine_notice_files_text(files_with_same_hash, input_dir, txt_output_file, file_title) if __name__ == "__main__": diff --git a/tools/getb64key.py b/tools/getb64key.py new file mode 100755 index 0000000..976a157 --- /dev/null +++ b/tools/getb64key.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import base64 +import sys + +pkFile = open(sys.argv[1], 'rb').readlines() +base64Key = "" +inCert = False +for line in pkFile: + if line.startswith(b"-"): + inCert = not inCert + continue + + base64Key += line.strip() + +print(base64.b16encode(base64.b64decode(base64Key)).lower()) diff --git a/tools/java-event-log-tags.py b/tools/java-event-log-tags.py index f364751..24bad3c 100755 --- a/tools/java-event-log-tags.py +++ b/tools/java-event-log-tags.py @@ -23,37 +23,43 @@ tags in the given input file. -h to display this usage message and exit. """ -import cStringIO +from __future__ import print_function + import getopt import os import os.path import re import sys +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO + import event_log_tags output_file = None try: opts, args = getopt.getopt(sys.argv[1:], "ho:") -except getopt.GetoptError, err: - print str(err) - print __doc__ +except getopt.GetoptError as err: + print(str(err)) + print(__doc__) sys.exit(2) for o, a in opts: if o == "-h": - print __doc__ + print(__doc__) sys.exit(2) elif o == "-o": output_file = a else: - print >> sys.stderr, "unhandled option %s" % (o,) + print("unhandled option %s" % o, file=sys.stderr) sys.exit(1) if len(args) != 2: - print "need exactly two input files, not %d" % (len(args),) - print __doc__ + print("need exactly two input files, not %d" % len(args)) + print(__doc__) sys.exit(1) fn = args[0] @@ -85,10 +91,10 @@ if "javadoc_hide" in tagfile.options: if tagfile.errors: for fn, ln, msg in tagfile.errors: - print >> sys.stderr, "%s:%d: error: %s" % (fn, ln, msg) + print("%s:%d: error: %s" % (fn, ln, msg), file=sys.stderr) sys.exit(1) -buffer = cStringIO.StringIO() +buffer = StringIO() buffer.write("/* This file is auto-generated. DO NOT MODIFY.\n" " * Source file: %s\n" " */\n\n" % (fn,)) @@ -143,7 +149,7 @@ for t in tagfile.tags: buffer.write("\n }\n") -buffer.write("}\n"); +buffer.write("}\n") output_dir = os.path.dirname(output_file) if not os.path.exists(output_dir): diff --git a/tools/java-layers.py b/tools/java-layers.py index b3aec2b..3b9d802 100755 --- a/tools/java-layers.py +++ b/tools/java-layers.py @@ -1,9 +1,18 @@ #!/usr/bin/env python +from __future__ import print_function + import os import re import sys + +def itervalues(obj): + if hasattr(obj, 'itervalues'): + return obj.itervalues() + return obj.values() + + def fail_with_usage(): sys.stderr.write("usage: java-layers.py DEPENDENCY_FILE SOURCE_DIRECTORIES...\n") sys.stderr.write("\n") @@ -69,27 +78,27 @@ class Dependencies: if upper in deps: recurse(obj, deps[upper], visited) self.deps = deps - self.parts = [(dep.lower.split('.'),dep) for dep in deps.itervalues()] + self.parts = [(dep.lower.split('.'),dep) for dep in itervalues(deps)] # transitive closure of dependencies - for dep in deps.itervalues(): + for dep in itervalues(deps): recurse(dep, dep, []) # disallow everything from the low level components - for dep in deps.itervalues(): + for dep in itervalues(deps): if dep.lowlevel: - for d in deps.itervalues(): + for d in itervalues(deps): if dep != d and not d.legacy: dep.transitive.add(d.lower) # disallow the 'top' components everywhere but in their own package - for dep in deps.itervalues(): + for dep in itervalues(deps): if dep.top and not dep.legacy: - for d in deps.itervalues(): + for d in itervalues(deps): if dep != d and not d.legacy: d.transitive.add(dep.lower) - for dep in deps.itervalues(): + for dep in itervalues(deps): dep.transitive = set([x+"." for x in dep.transitive]) if False: - for dep in deps.itervalues(): - print "-->", dep.lower, "-->", dep.transitive + for dep in itervalues(deps): + print("-->", dep.lower, "-->", dep.transitive) # Lookup the dep object for the given package. If pkg is a subpackage # of one with a rule, that one will be returned. If no matches are found, @@ -117,7 +126,7 @@ class Dependencies: def parse_dependency_file(filename): global err - f = file(filename) + f = open(filename) lines = f.readlines() f.close() def lineno(s, i): @@ -171,7 +180,7 @@ def find_java_files(srcs): result = [] for d in srcs: if d[0] == '@': - f = file(d[1:]) + f = open(d[1:]) result.extend([fn for fn in [s.strip() for s in f.readlines()] if len(fn) != 0]) f.close() @@ -188,7 +197,7 @@ IMPORT = re.compile("import\s+(.*)") def examine_java_file(deps, filename): global err # Yes, this is a crappy java parser. Write a better one if you want to. - f = file(filename) + f = open(filename) text = f.read() f.close() text = COMMENTS.sub("", text) @@ -218,8 +227,8 @@ def examine_java_file(deps, filename): imports.append(m.group(1)) # Do the checking if False: - print filename - print "'%s' --> %s" % (pkg, imports) + print(filename) + print("'%s' --> %s" % (pkg, imports)) dep = deps.lookup(pkg) if not dep: sys.stderr.write(("%s: Error: Package does not appear in dependency file: " diff --git a/tools/merge-event-log-tags.py b/tools/merge-event-log-tags.py index 64bad3f..7790048 100755 --- a/tools/merge-event-log-tags.py +++ b/tools/merge-event-log-tags.py @@ -24,7 +24,8 @@ and fails if they do. -h to display this usage message and exit. """ -import cStringIO +from __future__ import print_function + import getopt try: import hashlib @@ -33,8 +34,20 @@ except ImportError: import struct import sys +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO + import event_log_tags + +def iteritems(obj): + if hasattr(obj, 'iteritems'): + return obj.iteritems() + return obj.items() + + errors = [] warnings = [] @@ -48,21 +61,21 @@ ASSIGN_LIMIT = 1000000 try: opts, args = getopt.getopt(sys.argv[1:], "ho:m:") -except getopt.GetoptError, err: - print str(err) - print __doc__ +except getopt.GetoptError as err: + print(str(err)) + print(__doc__) sys.exit(2) for o, a in opts: if o == "-h": - print __doc__ + print(__doc__) sys.exit(2) elif o == "-o": output_file = a elif o == "-m": pre_merged_file = a else: - print >> sys.stderr, "unhandled option %s" % (o,) + print("unhandled option %s" % (o,), file=sys.stderr) sys.exit(1) # Restrictions on tags: @@ -133,12 +146,12 @@ for fn in args: if errors: for fn, ln, msg in errors: - print >> sys.stderr, "%s:%d: error: %s" % (fn, ln, msg) + print("%s:%d: error: %s" % (fn, ln, msg), file=sys.stderr) sys.exit(1) if warnings: for fn, ln, msg in warnings: - print >> sys.stderr, "%s:%d: warning: %s" % (fn, ln, msg) + print("%s:%d: warning: %s" % (fn, ln, msg), file=sys.stderr) # Python's hash function (a) isn't great and (b) varies between # versions of python. Using md5 is overkill here but is the same from @@ -154,14 +167,14 @@ def hashname(str): # If we were provided pre-merged tags (w/ the -m option), then don't # ever try to allocate one, just fail if we don't have a number -for name, t in sorted(by_tagname.iteritems()): +for name, t in sorted(iteritems(by_tagname)): if t.tagnum is None: if pre_merged_tags: try: t.tagnum = pre_merged_tags[t.tagname] except KeyError: - print >> sys.stderr, ("Error: Tag number not defined for tag `%s'." - +" Have you done a full build?") % t.tagname + print("Error: Tag number not defined for tag `%s'." + " Have you done a full build?" % t.tagname, file=sys.stderr) sys.exit(1) else: while True: @@ -174,8 +187,8 @@ for name, t in sorted(by_tagname.iteritems()): # by_tagnum should be complete now; we've assigned numbers to all tags. -buffer = cStringIO.StringIO() -for n, t in sorted(by_tagnum.iteritems()): +buffer = StringIO() +for n, t in sorted(iteritems(by_tagnum)): if t.description: buffer.write("%d %s %s\n" % (t.tagnum, t.tagname, t.description)) else: diff --git a/tools/parsedeps.py b/tools/parsedeps.py index 32d8ad7..d36442b 100755 --- a/tools/parsedeps.py +++ b/tools/parsedeps.py @@ -1,10 +1,16 @@ #!/usr/bin/env python # vim: ts=2 sw=2 +from __future__ import print_function + import optparse -import re import sys +try: + raw_input +except NameError: + raw_input = input + class Dependency: def __init__(self, tgt): @@ -43,13 +49,15 @@ class Dependencies: t.pos = pos def get(self, tgt): - if self.lines.has_key(tgt): + if tgt in self.lines: return self.lines[tgt] else: return None def __iter__(self): - return self.lines.iteritems() + if hasattr(self.lines, 'iteritems'): + return self.lines.iteritems() + return iter(self.lines.items()) def trace(self, tgt, prereq): self.__visit = self.__visit + 1 @@ -73,9 +81,9 @@ class Dependencies: return result def help(): - print "Commands:" - print " dep TARGET Print the prerequisites for TARGET" - print " trace TARGET PREREQ Print the paths from TARGET to PREREQ" + print("Commands:") + print(" dep TARGET Print the prerequisites for TARGET") + print(" trace TARGET PREREQ Print the paths from TARGET to PREREQ") def main(argv): @@ -87,7 +95,7 @@ def main(argv): deps = Dependencies() filename = args[0] - print "Reading %s" % filename + print("Reading %s" % filename) if True: f = open(filename) @@ -106,7 +114,7 @@ def main(argv): deps.add(tgt, prereq) f.close() - print "Read %d dependencies. %d targets." % (deps.count, len(deps.lines)) + print("Read %d dependencies. %d targets." % (deps.count, len(deps.lines))) while True: line = raw_input("target> ") if not line.strip(): @@ -118,12 +126,12 @@ def main(argv): d = deps.get(tgt) if d: for prereq in d.prereqs: - print prereq.tgt + print(prereq.tgt) elif len(split) == 3 and cmd == "trace": tgt = split[1] prereq = split[2] if False: - print "from %s to %s" % (tgt, prereq) + print("from %s to %s" % (tgt, prereq)) trace = deps.trace(tgt, prereq) if trace: width = 0 @@ -134,10 +142,10 @@ def main(argv): for g in trace: for t in g: if t.pos: - print t.tgt, " " * (width-len(t.tgt)), " #", t.pos + print(t.tgt, " " * (width-len(t.tgt)), " #", t.pos) else: - print t.tgt - print + print(t.tgt) + print() else: help() @@ -145,7 +153,6 @@ if __name__ == "__main__": try: main(sys.argv) except KeyboardInterrupt: - print + print() except EOFError: - print - + print() diff --git a/tools/post_process_props.py b/tools/post_process_props.py index fa6106f..64af01d 100755 --- a/tools/post_process_props.py +++ b/tools/post_process_props.py @@ -16,6 +16,13 @@ import sys + +def iteritems(obj): + if hasattr(obj, 'iteritems'): + return obj.iteritems() + return obj.items() + + # Usage: post_process_props.py file.prop [blacklist_key, ...] # Blacklisted keys are removed from the property file, if present @@ -27,15 +34,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" @@ -46,7 +60,7 @@ def mangle_default_prop(prop): # default to "adb". That might not the right policy there, but it's better # to be explicit. if not prop.get("persist.sys.usb.config"): - prop.put("persist.sys.usb.config", "none"); + prop.put("persist.sys.usb.config", "none") def validate(prop): """Validate the properties. @@ -56,7 +70,7 @@ def validate(prop): """ check_pass = True buildprops = prop.to_dict() - for key, value in buildprops.iteritems(): + for key, value in iteritems(buildprops): # Check build properties' length. if len(key) > PROP_NAME_MAX: check_pass = False @@ -110,6 +124,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 +135,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 +146,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/product_debug.py b/tools/product_debug.py index ff2657c..1433a9a 100755 --- a/tools/product_debug.py +++ b/tools/product_debug.py @@ -14,13 +14,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os +from __future__ import print_function + +from operator import itemgetter import re import sys + +def iteritems(obj): + if hasattr(obj, 'iteritems'): + return obj.iteritems() + return obj.items() + + def break_lines(key, val): # these don't get split - if key in ("PRODUCT_MODEL"): + if key in ("PRODUCT_MODEL",): return (key,val) return (key, "\n".join(val.split())) @@ -42,8 +51,7 @@ def parse_variables(lines): def render_variables(variables): variables = dict(variables) del variables["FILE"] - variables = list(variables.iteritems()) - variables.sort(lambda a, b: cmp(a[0], b[0])) + variables = sorted(variables.items(), key=itemgetter(0)) return ("<table id='variables'>" + "\n".join([ "<tr><th>%(key)s</th><td>%(val)s</td></tr>" % { "key": key, "val": val } for key,val in variables]) @@ -69,7 +77,7 @@ def render_original(variables, text): return text def read_file(fn): - f = file(fn) + f = open(fn) text = f.read() f.close() return text @@ -104,7 +112,7 @@ def main(argv): "variables": render_variables(variables), "original": render_original(variables, original), }) - print """<html> + print("""<html> <head> @@ -153,7 +161,7 @@ def main(argv): %(variables)s </body> </html> -""" % values +""" % values) if __name__ == "__main__": main(sys.argv) diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py index f2bf1e1..b132963 100755 --- a/tools/releasetools/add_img_to_target_files.py +++ b/tools/releasetools/add_img_to_target_files.py @@ -22,10 +22,12 @@ add them to the zipfile. Usage: add_img_to_target_files target_files """ +from __future__ import print_function + import sys if sys.hexversion < 0x02070000: - print >> sys.stderr, "Python 2.7 or newer is required." + print("Python 2.7 or newer is required.", file=sys.stderr) sys.exit(1) import datetime @@ -51,7 +53,7 @@ def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None): prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system.img") if os.path.exists(prebuilt_path): - print "system.img already exists in %s, no need to rebuild..." % (prefix,) + print("system.img already exists in %s, no need to rebuild..." % prefix) return def output_sink(fn, data): @@ -60,7 +62,7 @@ def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None): ofile.close() if OPTIONS.rebuild_recovery: - print "Building new recovery patch" + print("Building new recovery patch") common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img, boot_img, info_dict=OPTIONS.info_dict) @@ -83,7 +85,7 @@ def AddVendor(output_zip, prefix="IMAGES/"): prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "vendor.img") if os.path.exists(prebuilt_path): - print "vendor.img already exists in %s, no need to rebuild..." % (prefix,) + print("vendor.img already exists in %s, no need to rebuild..." % prefix) return block_list = common.MakeTempFile(prefix="vendor-blocklist-", suffix=".map") @@ -100,7 +102,7 @@ def BuildVendor(input_dir, info_dict, block_list=None): def CreateImage(input_dir, info_dict, what, block_list=None): - print "creating " + what + ".img..." + print("creating " + what + ".img...") img = common.MakeTempFile(prefix=what + "-", suffix=".img") @@ -167,7 +169,7 @@ def AddUserdata(output_zip, prefix="IMAGES/"): prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "userdata.img") if os.path.exists(prebuilt_path): - print "userdata.img already exists in %s, no need to rebuild..." % (prefix,) + print("userdata.img already exists in %s, no need to rebuild..." % prefix) return image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, @@ -178,7 +180,7 @@ def AddUserdata(output_zip, prefix="IMAGES/"): not image_props.get("partition_size")): return - print "creating userdata.img..." + print("creating userdata.img...") # Use a fixed timestamp (01/01/2009) when packaging the image. # Bug: 24377993 @@ -207,12 +209,59 @@ def AddUserdata(output_zip, prefix="IMAGES/"): os.rmdir(temp_dir) +def AddUserdataExtra(output_zip, prefix="IMAGES/"): + """Create extra userdata image and store it in output_zip.""" + + image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, + "data_extra") + + # The build system has to explicitly request extra userdata. + if "fs_type" not in image_props: + return + + extra_name = image_props.get("partition_name", "extra") + + prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "userdata_%s.img" % extra_name) + if os.path.exists(prebuilt_path): + print("userdata_%s.img already exists in %s, no need to rebuild..." % (extra_name, prefix,)) + return + + # We only allow yaffs to have a 0/missing partition_size. + # Extfs, f2fs must have a size. Skip userdata_extra.img if no size. + if (not image_props.get("fs_type", "").startswith("yaffs") and + not image_props.get("partition_size")): + return + + 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, prefix + "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.""" prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "cache.img") if os.path.exists(prebuilt_path): - print "cache.img already exists in %s, no need to rebuild..." % (prefix,) + print("cache.img already exists in %s, no need to rebuild..." % prefix) return image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, @@ -221,7 +270,7 @@ def AddCache(output_zip, prefix="IMAGES/"): if "fs_type" not in image_props: return - print "creating cache.img..." + print("creating cache.img...") # Use a fixed timestamp (01/01/2009) when packaging the image. # Bug: 24377993 @@ -256,7 +305,7 @@ def AddImagesToTargetFiles(filename): if not OPTIONS.add_missing: for n in input_zip.namelist(): if n.startswith("IMAGES/"): - print "target_files appears to already contain images." + print("target_files appears to already contain images.") sys.exit(1) try: @@ -275,13 +324,13 @@ def AddImagesToTargetFiles(filename): compression=zipfile.ZIP_DEFLATED) def banner(s): - print "\n\n++++ " + s + " ++++\n\n" + print("\n\n++++ " + s + " ++++\n\n") banner("boot") prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img") boot_image = None if os.path.exists(prebuilt_path): - print "boot.img already exists in IMAGES/, no need to rebuild..." + print("boot.img already exists in IMAGES/, no need to rebuild...") if OPTIONS.rebuild_recovery: boot_image = common.GetBootableImage( "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") @@ -295,7 +344,7 @@ def AddImagesToTargetFiles(filename): recovery_image = None prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img") if os.path.exists(prebuilt_path): - print "recovery.img already exists in IMAGES/, no need to rebuild..." + print("recovery.img already exists in IMAGES/, no need to rebuild...") if OPTIONS.rebuild_recovery: recovery_image = common.GetBootableImage( "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") @@ -312,6 +361,8 @@ def AddImagesToTargetFiles(filename): AddVendor(output_zip) banner("userdata") AddUserdata(output_zip) + banner("extrauserdata") + AddUserdataExtra(output_zip) banner("cache") AddCache(output_zip) @@ -347,16 +398,16 @@ def main(argv): sys.exit(1) AddImagesToTargetFiles(args[0]) - print "done." + print("done.") if __name__ == '__main__': try: common.CloseInheritedPipes() main(sys.argv[1:]) except common.ExternalError as e: - print - print " ERROR: %s" % (e,) - print + print() + print(" ERROR: %s" % e) + print() sys.exit(1) finally: common.Cleanup() diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py index cb6fc85..bb2f16d 100644 --- a/tools/releasetools/blockimgdiff.py +++ b/tools/releasetools/blockimgdiff.py @@ -379,7 +379,7 @@ class BlockImageDiff(object): src_str.append("%s:%s" % (sh, sr.to_string_raw())) stashes[sh] -= 1 if stashes[sh] == 0: - free_string.append("free %s\n" % (sh)) + free_string.append("free %s\n" % sh) stashes.pop(sh) heapq.heappush(free_stash_ids, sid) @@ -483,7 +483,7 @@ class BlockImageDiff(object): if free_string: out.append("".join(free_string)) - if self.version >= 2: + if self.version >= 2 and common.OPTIONS.cache_size is not None: # Sanity check: abort if we're going to need more stash space than # the allowed size (cache_size * threshold). There are two purposes # of having a threshold here. a) Part of the cache may have been @@ -524,10 +524,16 @@ class BlockImageDiff(object): if self.version >= 2: max_stashed_size = max_stashed_blocks * self.tgt.blocksize - max_allowed = common.OPTIONS.cache_size * common.OPTIONS.stash_threshold - print("max stashed blocks: %d (%d bytes), limit: %d bytes (%.2f%%)\n" % ( - max_stashed_blocks, max_stashed_size, max_allowed, - max_stashed_size * 100.0 / max_allowed)) + OPTIONS = common.OPTIONS + if OPTIONS.cache_size is not None: + max_allowed = OPTIONS.cache_size * OPTIONS.stash_threshold + print("max stashed blocks: %d (%d bytes), " + "limit: %d bytes (%.2f%%)\n" % ( + max_stashed_blocks, max_stashed_size, max_allowed, + max_stashed_size * 100.0 / max_allowed)) + else: + print("max stashed blocks: %d (%d bytes), limit: <unknown>\n" % ( + max_stashed_blocks, max_stashed_size)) def ReviseStashSize(self): print("Revising stash size...") diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py index 357a666..efaf7eb 100755 --- a/tools/releasetools/build_image.py +++ b/tools/releasetools/build_image.py @@ -20,16 +20,23 @@ Build image output_image_file from input_directory and properties_file. Usage: build_image input_directory properties_file output_image_file """ + +from __future__ import print_function + import os import os.path import re import subprocess import sys -import commands import common import shutil import tempfile +try: + from commands import getstatusoutput +except ImportError: + from subprocess import getstatusoutput + OPTIONS = common.OPTIONS FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7" @@ -42,18 +49,18 @@ def RunCommand(cmd): Returns: A tuple of the output and the exit code. """ - print "Running: ", " ".join(cmd) + print("Running: %s" % " ".join(cmd)) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output, _ = p.communicate() - print "%s" % (output.rstrip(),) + print("%s" % output.rstrip()) return (output, p.returncode) def GetVerityTreeSize(partition_size): cmd = "build_verity_tree -s %d" cmd %= partition_size - status, output = commands.getstatusoutput(cmd) + status, output = getstatusoutput(cmd) if status: - print output + print(output) return False, 0 return True, int(output) @@ -61,9 +68,9 @@ def GetVerityMetadataSize(partition_size): cmd = "system/extras/verity/build_verity_metadata.py -s %d" cmd %= partition_size - status, output = commands.getstatusoutput(cmd) + status, output = getstatusoutput(cmd) if status: - print output + print(output) return False, 0 return True, int(output) @@ -87,10 +94,10 @@ def AdjustPartitionSizeForVerity(partition_size): def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict): cmd = "build_verity_tree -A %s %s %s" % ( FIXED_SALT, sparse_image_path, verity_image_path) - print cmd - status, output = commands.getstatusoutput(cmd) + print(cmd) + status, output = getstatusoutput(cmd) if status: - print "Could not build verity tree! Error: %s" % output + print("Could not build verity tree! Error: %s" % output) return False root, salt = output.split() prop_dict["verity_root_hash"] = root @@ -103,10 +110,10 @@ def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, "system/extras/verity/build_verity_metadata.py %s %s %s %s %s %s %s") cmd = cmd_template % (image_size, verity_metadata_path, root_hash, salt, block_device, signer_path, key) - print cmd - status, output = commands.getstatusoutput(cmd) + print(cmd) + status, output = getstatusoutput(cmd) if status: - print "Could not build verity metadata! Error: %s" % output + print("Could not build verity metadata! Error: %s" % output) return False return True @@ -121,10 +128,10 @@ def Append2Simg(sparse_image_path, unsparse_image_path, error_message): """ cmd = "append2simg %s %s" cmd %= (sparse_image_path, unsparse_image_path) - print cmd - status, output = commands.getstatusoutput(cmd) + print(cmd) + status, output = getstatusoutput(cmd) if status: - print "%s: %s" % (error_message, output) + print("%s: %s" % (error_message, output)) return False return True @@ -263,9 +270,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"]]) @@ -344,7 +355,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): ext4fs_stats = re.compile( r'Created filesystem with .* (?P<used_blocks>[0-9]+)/' r'(?P<total_blocks>[0-9]+) blocks') - m = ext4fs_stats.match(ext4fs_output.strip().split('\n')[-1]) + m = ext4fs_stats.match(ext4fs_output.strip().split(b'\n')[-1]) used_blocks = int(m.groupdict().get('used_blocks')) total_blocks = int(m.groupdict().get('total_blocks')) reserved_blocks = min(4096, int(total_blocks * 0.02)) @@ -367,7 +378,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): return False if verity_supported and is_verity_partition: if 2 * image_size - AdjustPartitionSizeForVerity(image_size) > partition_size: - print "Error: No more room on %s to fit verity data" % mount_point + print("Error: No more room on %s to fit verity data" % mount_point) return False prop_dict["original_partition_size"] = prop_dict["partition_size"] prop_dict["partition_size"] = str(image_size) @@ -440,6 +451,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") @@ -474,7 +490,7 @@ def LoadGlobalDict(filename): def main(argv): if len(argv) != 4: - print __doc__ + print(__doc__) sys.exit(1) in_dir = argv[0] @@ -501,14 +517,14 @@ def main(argv): elif image_filename == "oem.img": mount_point = "oem" else: - print >> sys.stderr, "error: unknown image file name ", image_filename + print("error: unknown image file name ", image_filename, file=sys.stderr) exit(1) image_properties = ImagePropFromGlobalDict(glob_dict, mount_point) if not BuildImage(in_dir, image_properties, out_file, target_out): - print >> sys.stderr, "error: failed to build %s from %s" % (out_file, - in_dir) + print("error: failed to build %s from %s" % (out_file, in_dir), + file=sys.stderr) exit(1) diff --git a/tools/releasetools/check_target_files_signatures.py b/tools/releasetools/check_target_files_signatures.py index 5c541ab..df3be2f 100755 --- a/tools/releasetools/check_target_files_signatures.py +++ b/tools/releasetools/check_target_files_signatures.py @@ -39,10 +39,12 @@ Usage: check_target_file_signatures [flags] target_files """ +from __future__ import print_function + import sys if sys.hexversion < 0x02070000: - print >> sys.stderr, "Python 2.7 or newer is required." + print("Python 2.7 or newer is required.", file=sys.stderr) sys.exit(1) import os @@ -53,6 +55,13 @@ import zipfile import common + +def iteritems(obj): + if hasattr(obj, 'iteritems'): + return obj.iteritems() + return obj.items() + + # Work around a bug in python's zipfile module that prevents opening # of zipfiles if any entry has an extra field of between 1 and 3 bytes # (which is common with zipaligned APKs). This overrides the @@ -81,9 +90,9 @@ def Pop(): def Banner(msg): - print "-" * 70 - print " ", msg - print "-" * 70 + print("-" * 70) + print(" ", msg) + print("-" * 70) def GetCertSubject(cert): @@ -260,7 +269,7 @@ class TargetFiles(object): """Look for any instances where packages signed with different certs request the same sharedUserId.""" apks_by_uid = {} - for apk in self.apks.itervalues(): + for apk in self.apks.values(): if apk.shared_uid: apks_by_uid.setdefault(apk.shared_uid, []).append(apk) @@ -275,15 +284,15 @@ class TargetFiles(object): AddProblem("different cert sets for packages with uid %s" % (uid,)) - print "uid %s is shared by packages with different cert sets:" % (uid,) + print("uid %s is shared by packages with different cert sets:" % uid) for apk in apks: - print "%-*s [%s]" % (self.max_pkg_len, apk.package, apk.filename) + print("%-*s [%s]" % (self.max_pkg_len, apk.package, apk.filename)) for cert in apk.certs: - print " ", ALL_CERTS.Get(cert) - print + print(" ", ALL_CERTS.Get(cert)) + print() def CheckExternalSignatures(self): - for apk_filename, certname in self.certmap.iteritems(): + for apk_filename, certname in iteritems(self.certmap): if certname == "EXTERNAL": # Apps marked EXTERNAL should be signed with the test key # during development, then manually re-signed after @@ -299,26 +308,26 @@ class TargetFiles(object): def PrintCerts(self): """Display a table of packages grouped by cert.""" by_cert = {} - for apk in self.apks.itervalues(): + for apk in self.apks.values(): for cert in apk.certs: by_cert.setdefault(cert, []).append((apk.package, apk)) - order = [(-len(v), k) for (k, v) in by_cert.iteritems()] + order = [(-len(v), k) for (k, v) in iteritems(by_cert)] order.sort() for _, cert in order: - print "%s:" % (ALL_CERTS.Get(cert),) + print("%s:" % ALL_CERTS.Get(cert)) apks = by_cert[cert] apks.sort() for _, apk in apks: if apk.shared_uid: - print " %-*s %-*s [%s]" % (self.max_fn_len, apk.filename, + print(" %-*s %-*s [%s]" % (self.max_fn_len, apk.filename, self.max_pkg_len, apk.package, - apk.shared_uid) + apk.shared_uid)) else: - print " %-*s %-*s" % (self.max_fn_len, apk.filename, - self.max_pkg_len, apk.package) - print + print(" %-*s %-*s" % (self.max_fn_len, apk.filename, + self.max_pkg_len, apk.package)) + print() def CompareWith(self, other): """Look for instances where a given package that exists in both @@ -339,12 +348,12 @@ class TargetFiles(object): by_certpair.setdefault((other.apks[i].certs, self.apks[i].certs), []).append(i) else: - print "%s [%s]: new APK (not in comparison target_files)" % ( - i, self.apks[i].filename) + print("%s [%s]: new APK (not in comparison target_files)" % ( + i, self.apks[i].filename)) else: if i in other.apks: - print "%s [%s]: removed APK (only in comparison target_files)" % ( - i, other.apks[i].filename) + print("%s [%s]: removed APK (only in comparison target_files)" % ( + i, other.apks[i].filename)) if by_certpair: AddProblem("some APKs changed certs") @@ -352,23 +361,23 @@ class TargetFiles(object): for (old, new), packages in sorted(by_certpair.items()): for i, o in enumerate(old): if i == 0: - print "was", ALL_CERTS.Get(o) + print("was", ALL_CERTS.Get(o)) else: - print " ", ALL_CERTS.Get(o) + print(" ", ALL_CERTS.Get(o)) for i, n in enumerate(new): if i == 0: - print "now", ALL_CERTS.Get(n) + print("now", ALL_CERTS.Get(n)) else: - print " ", ALL_CERTS.Get(n) + print(" ", ALL_CERTS.Get(n)) for i in sorted(packages): old_fn = other.apks[i].filename new_fn = self.apks[i].filename if old_fn == new_fn: - print " %-*s [%s]" % (max_pkg_len, i, old_fn) + print(" %-*s [%s]" % (max_pkg_len, i, old_fn)) else: - print " %-*s [was: %s; now: %s]" % (max_pkg_len, i, - old_fn, new_fn) - print + print(" %-*s [was: %s; now: %s]" % (max_pkg_len, i, + old_fn, new_fn)) + print() def main(argv): @@ -423,9 +432,9 @@ def main(argv): target_files.CompareWith(compare_files) if PROBLEMS: - print "%d problem(s) found:\n" % (len(PROBLEMS),) + print("%d problem(s) found:\n" % len(PROBLEMS)) for p in PROBLEMS: - print p + print(p) return 1 return 0 @@ -436,7 +445,7 @@ if __name__ == '__main__': r = main(sys.argv[1:]) sys.exit(r) except common.ExternalError as e: - print - print " ERROR: %s" % (e,) - print + print() + print(" ERROR: %s" % e) + print() sys.exit(1) diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index 2965fa8..26cc674 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function + import copy import errno import getopt @@ -34,6 +36,17 @@ import rangelib from hashlib import sha1 as sha1 +try: + raw_input +except NameError: + raw_input = input + + +def iteritems(obj): + if hasattr(obj, 'iteritems'): + return obj.iteritems() + return obj.items() + class Options(object): def __init__(self): @@ -70,6 +83,10 @@ OPTIONS = Options() # Values for "certificate" in apkcerts that mean special things. SPECIAL_CERT_STRINGS = ("PRESIGNED", "EXTERNAL") +# Stash size cannot exceed cache_size * threshold. +OPTIONS.cache_size = None +OPTIONS.stash_threshold = 0.8 + class ExternalError(RuntimeError): pass @@ -79,7 +96,7 @@ def Run(args, **kwargs): """Create and return a subprocess.Popen object, printing the command line on the terminal if -v was specified.""" if OPTIONS.verbose: - print " running: ", " ".join(args) + print(" running: ", " ".join(args)) return subprocess.Popen(args, **kwargs) @@ -150,6 +167,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"): @@ -179,7 +198,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 @@ -187,7 +206,7 @@ def LoadBuildProp(read_helper): try: data = read_helper("SYSTEM/build.prop") except KeyError: - print "Warning: could not find SYSTEM/build.prop in %s" % zip + print("Warning: could not find SYSTEM/build.prop in %s" % zip) data = "" return LoadDictionaryFromLines(data.split("\n")) @@ -202,7 +221,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 @@ -215,7 +234,7 @@ def LoadRecoveryFSTab(read_helper, fstab_version): try: data = read_helper("RECOVERY/RAMDISK/etc/recovery.fstab") except KeyError: - print "Warning: could not find RECOVERY/RAMDISK/etc/recovery.fstab" + print("Warning: could not find RECOVERY/RAMDISK/etc/recovery.fstab") data = "" if fstab_version == 1: @@ -247,11 +266,12 @@ def LoadRecoveryFSTab(read_helper, fstab_version): if i.startswith("length="): length = int(i[7:]) else: - print "%s: unknown option \"%s\"" % (mount_point, i) + print("%s: unknown option \"%s\"" % (mount_point, i)) - d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[1], - device=pieces[2], length=length, - device2=device2) + if not d.get(mount_point): + d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[1], + device=pieces[2], length=length, + device2=device2) elif fstab_version == 2: d = {} @@ -287,9 +307,10 @@ def LoadRecoveryFSTab(read_helper, fstab_version): context = i mount_point = pieces[1] - d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2], - device=pieces[0], length=length, - device2=None, context=context) + if not d.get(mount_point): + d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2], + device=pieces[0], length=length, + device2=None, context=context) else: raise ValueError("Unknown fstab_version: \"%d\"" % (fstab_version,)) @@ -299,7 +320,7 @@ def LoadRecoveryFSTab(read_helper, fstab_version): def DumpInfoDict(d): for k, v in sorted(d.items()): - print "%-25s = (%s) %s" % (k, type(v).__name__, v) + print("%-25s = (%s) %s" % (k, type(v).__name__, v)) def BuildBootableImage(sourcedir, fs_config_file, info_dict=None): @@ -331,44 +352,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") + """check if uboot is requested""" + fn = os.path.join(sourcedir, "ubootargs") if os.access(fn, os.F_OK): - cmd.append("--cmdline") - 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) - 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") - 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, + # 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") + if os.access(fn, os.F_OK): + cmd.append("--dt") + cmd.append(fn) + + 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" % ( @@ -423,15 +474,15 @@ def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir, prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name) if os.path.exists(prebuilt_path): - print "using prebuilt %s from BOOTABLE_IMAGES..." % (prebuilt_name,) + print("using prebuilt %s from BOOTABLE_IMAGES..." % prebuilt_name) return File.FromLocalFile(name, prebuilt_path) prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name) if os.path.exists(prebuilt_path): - print "using prebuilt %s from IMAGES..." % (prebuilt_name,) + print("using prebuilt %s from IMAGES..." % prebuilt_name) return File.FromLocalFile(name, prebuilt_path) - print "building image from target_files %s..." % (tree_subdir,) + print("building image from target_files %s..." % tree_subdir) fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt" data = BuildBootableImage(os.path.join(unpack_dir, tree_subdir), os.path.join(unpack_dir, fs_config), @@ -455,6 +506,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) @@ -509,7 +561,7 @@ def GetKeyPasswords(keylist): if p.returncode == 0: # Encrypted key with empty string as password. key_passwords[k] = '' - elif stderr.startswith('Error decrypting key'): + elif stderr.startswith(b'Error decrypting key'): # Definitely encrypted key. # It would have said "Error reading key" if it didn't parse correctly. need_passwords.append(k) @@ -583,8 +635,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 @@ -604,11 +656,11 @@ def CheckSize(data, target, info_dict): if pct >= 99.0: raise ExternalError(msg) elif pct >= 95.0: - print - print " WARNING: ", msg - print + print() + print(" WARNING: ", msg) + print() elif OPTIONS.verbose: - print " ", msg + print(" ", msg) def ReadApkCerts(tf_zip): @@ -657,8 +709,8 @@ COMMON_DOCSTRING = """ """ def Usage(docstring): - print docstring.rstrip("\n") - print COMMON_DOCSTRING + print(docstring.rstrip("\n")) + print(COMMON_DOCSTRING) def ParseOptions(argv, @@ -682,7 +734,7 @@ def ParseOptions(argv, list(extra_long_opts)) except getopt.GetoptError as err: Usage(docstring) - print "**", str(err), "**" + print("**", str(err), "**") sys.exit(2) for o, a in opts: @@ -778,7 +830,7 @@ class PasswordManager(object): current[i] = "" if not first: - print "key file %s still missing some passwords." % (self.pwfile,) + print("key file %s still missing some passwords." % self.pwfile) answer = raw_input("try to edit again? [y]> ").strip() if answer and answer[0] not in 'yY': raise RuntimeError("key passwords unavailable") @@ -792,7 +844,7 @@ class PasswordManager(object): values. """ result = {} - for k, v in sorted(current.iteritems()): + for k, v in sorted(iteritems(current)): if v: result[k] = v else: @@ -813,7 +865,7 @@ class PasswordManager(object): f.write("# (Additional spaces are harmless.)\n\n") first_line = None - sorted_list = sorted([(not v, k, v) for (k, v) in current.iteritems()]) + sorted_list = sorted((not v, k, v) for (k, v) in current.items()) for i, (_, k, v) in enumerate(sorted_list): f.write("[[[ %s ]]] %s\n" % (v, k)) if not v and first_line is None: @@ -838,13 +890,13 @@ class PasswordManager(object): continue m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line) if not m: - print "failed to parse password file: ", line + print("failed to parse password file: ", line) else: result[m.group(2)] = m.group(1) f.close() except IOError as e: if e.errno != errno.ENOENT: - print "error reading password file: ", str(e) + print("error reading password file: ", str(e)) return result @@ -945,7 +997,7 @@ class DeviceSpecificParams(object): """Keyword arguments to the constructor become attributes of this object, which is passed to all functions in the device-specific module.""" - for k, v in kwargs.iteritems(): + for k, v in iteritems(kwargs): setattr(self, k, v) self.extras = OPTIONS.extras @@ -962,10 +1014,10 @@ class DeviceSpecificParams(object): if x == ".py": f = b info = imp.find_module(f, [d]) - print "loaded device-specific extensions from", path + print("loaded device-specific extensions from", path) self.module = imp.load_module("device_specific", *info) except ImportError: - print "unable to load device-specific module; assuming none" + print("unable to load device-specific module; assuming none") def _DoCall(self, function_name, *args, **kwargs): """Call the named function in the device-specific module, passing @@ -992,6 +1044,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 @@ -1096,7 +1153,7 @@ class Difference(object): th.start() th.join(timeout=300) # 5 mins if th.is_alive(): - print "WARNING: diff command timed out" + print("WARNING: diff command timed out") p.terminate() th.join(5) if th.is_alive(): @@ -1104,8 +1161,8 @@ class Difference(object): th.join() if err or p.returncode != 0: - print "WARNING: failure running %s:\n%s\n" % ( - diff_program, "".join(err)) + print("WARNING: failure running %s:\n%s\n" % ( + diff_program, "".join(err))) self.patch = None return None, None, None diff = ptemp.read() @@ -1127,7 +1184,7 @@ class Difference(object): def ComputeDifferences(diffs): """Call ComputePatch on all the Difference objects in 'diffs'.""" - print len(diffs), "diffs to compute" + print(len(diffs), "diffs to compute") # Do the largest files first, to try and reduce the long-pole effect. by_size = [(i.tf.size, i) for i in diffs] @@ -1153,13 +1210,13 @@ def ComputeDifferences(diffs): else: name = "%s (%s)" % (tf.name, sf.name) if patch is None: - print "patching failed! %s" % (name,) + print("patching failed! %s" % name) else: - print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % ( - dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name) + print("%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % ( + dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name)) lock.release() except Exception as e: - print e + print(e) raise # start worker threads; wait for them all to finish. @@ -1342,7 +1399,11 @@ PARTITION_TYPES = { "ext4": "EMMC", "emmc": "EMMC", "f2fs": "EMMC", - "squashfs": "EMMC" + "squashfs": "EMMC", + "ext2": "EMMC", + "ext3": "EMMC", + "vfat": "EMMC", + "osip": "OSIP" } def GetTypeAndDevice(mount_point, info): @@ -1356,16 +1417,18 @@ def GetTypeAndDevice(mount_point, info): def ParseCertificate(data): """Parse a PEM-format certificate.""" + from codecs import decode cert = [] save = False for line in data.split("\n"): if "--END CERTIFICATE--" in line: break if save: - cert.append(line) + l = line.encode() if hasattr(line, 'encode') else line + cert.append(l) if "--BEGIN CERTIFICATE--" in line: save = True - cert = "".join(cert).decode('base64') + cert = decode(b"".join(cert), 'base64') return cert def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img, @@ -1407,6 +1470,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 @@ -1441,10 +1508,9 @@ fi sh_location = m.group(1) found = True break - if found: - break + break - print "putting script in", sh_location + print("putting script in", sh_location) output_sink(sh_location, sh) diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py index a52e328..825a7eb 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.""" @@ -293,6 +323,10 @@ class EdifyGenerator(object): self.script.append( 'write_raw_image(package_extract_file("%(fn)s"), "%(device)s");' % args) + elif partition_type == "OSIP": + self.script.append( + 'write_osip_image(package_extract_file("%(fn)s"), "%(device)s");' + % args) elif partition_type == "EMMC": if mapfn: args["map"] = mapfn @@ -310,10 +344,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 +360,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 += ');' @@ -342,7 +376,7 @@ class EdifyGenerator(object): for d, l in symlink_list: by_dest.setdefault(d, []).append(l) - for dest, links in sorted(by_dest.iteritems()): + for dest, links in sorted(by_dest.items()): cmd = ('symlink("%s", ' % (dest,) + ",\0".join(['"' + i + '"' for i in sorted(links)]) + ");") self.script.append(self.WordWrap(cmd)) diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py index c486992..d486a7a 100755 --- a/tools/releasetools/img_from_target_files.py +++ b/tools/releasetools/img_from_target_files.py @@ -26,14 +26,20 @@ Usage: img_from_target_files [flags] input_target_files output_image_zip """ +from __future__ import print_function + import sys if sys.hexversion < 0x02070000: - print >> sys.stderr, "Python 2.7 or newer is required." + print("Python 2.7 or newer is required.", file=sys.stderr) sys.exit(1) +import errno import os +import re import shutil +import subprocess +import tempfile import zipfile import common @@ -47,6 +53,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 +103,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 @@ -116,7 +148,7 @@ def main(argv): recovery_image.AddToZip(output_zip) def banner(s): - print "\n\n++++ " + s + " ++++\n\n" + print("\n\n++++ " + s + " ++++\n\n") if not bootable_only: banner("AddSystem") @@ -129,15 +161,17 @@ def main(argv): pass # no vendor partition for this device banner("AddUserdata") add_img_to_target_files.AddUserdata(output_zip, prefix="") + banner("AddUserdataExtra") + add_img_to_target_files.AddUserdataExtra(output_zip, prefix="") banner("AddCache") add_img_to_target_files.AddCache(output_zip, prefix="") finally: - print "cleaning up..." + print("cleaning up...") common.ZipClose(output_zip) shutil.rmtree(OPTIONS.input_tmp) - print "done." + print("done.") if __name__ == '__main__': @@ -145,7 +179,7 @@ if __name__ == '__main__': common.CloseInheritedPipes() main(sys.argv[1:]) except common.ExternalError as e: - print - print " ERROR: %s" % (e,) - print + print() + print(" ERROR: %s" % e) + print() sys.exit(1) diff --git a/tools/releasetools/make_recovery_patch.py b/tools/releasetools/make_recovery_patch.py index 08d1450..7c6007e 100755 --- a/tools/releasetools/make_recovery_patch.py +++ b/tools/releasetools/make_recovery_patch.py @@ -14,10 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function + import sys if sys.hexversion < 0x02070000: - print >> sys.stderr, "Python 2.7 or newer is required." + print("Python 2.7 or newer is required.", file=sys.stderr) sys.exit(1) import os diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py index c5c16b4..bc40873 100755 --- a/tools/releasetools/ota_from_target_files.py +++ b/tools/releasetools/ota_from_target_files.py @@ -92,12 +92,26 @@ 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 + """ +from __future__ import print_function + import sys if sys.hexversion < 0x02070000: - print >> sys.stderr, "Python 2.7 or newer is required." + print("Python 2.7 or newer is required.", file=sys.stderr) sys.exit(1) import multiprocessing @@ -131,14 +145,14 @@ OPTIONS.oem_source = None OPTIONS.fallback_to_full = True OPTIONS.full_radio = False 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 value. Returns 'default' if the dict is empty.""" - x = [(v, k) for (k, v) in d.iteritems()] + x = [(v, k) for (k, v) in d.items()] if not x: return default x.sort() @@ -148,7 +162,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 @@ -257,14 +271,14 @@ class Item(object): def Dump(self, indent=0): if self.uid is not None: - print "%s%s %d %d %o" % ( - " " * indent, self.name, self.uid, self.gid, self.mode) + print("%s%s %d %d %o" % ( + " " * indent, self.name, self.uid, self.gid, self.mode)) else: - print "%s%s %s %s %s" % ( - " " * indent, self.name, self.uid, self.gid, self.mode) + print("%s%s %s %s %s" % ( + " " * indent, self.name, self.uid, self.gid, self.mode)) if self.is_dir: - print "%s%s" % (" "*indent, self.descendants) - print "%s%s" % (" "*indent, self.best_subtree) + print("%s%s" % (" "*indent, self.descendants)) + print("%s%s" % (" "*indent, self.best_subtree)) for i in self.children: i.Dump(indent=indent+1) @@ -288,7 +302,7 @@ class Item(object): d = self.descendants for i in self.children: if i.is_dir: - for k, v in i.CountChildMetadata().iteritems(): + for k, v in i.CountChildMetadata().items(): d[k] = d.get(k, 0) + v else: k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities) @@ -300,7 +314,7 @@ class Item(object): # First, find the (uid, gid) pair that matches the most # descendants. ug = {} - for (uid, gid, _, _, _, _), count in d.iteritems(): + for (uid, gid, _, _, _, _), count in d.items(): ug[(uid, gid)] = ug.get((uid, gid), 0) + count ug = MostPopularKey(ug, (0, 0)) @@ -310,7 +324,7 @@ class Item(object): best_fmode = (0, 0o644) best_selabel = (0, None) best_capabilities = (0, None) - for k, count in d.iteritems(): + for k, count in d.items(): if k[:2] != ug: continue if k[2] is not None and count >= best_dmode[0]: @@ -413,7 +427,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 +462,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" % ( @@ -467,11 +485,11 @@ def GetImage(which, tmpdir, info_dict): path = os.path.join(tmpdir, "IMAGES", which + ".img") mappath = os.path.join(tmpdir, "IMAGES", which + ".map") if os.path.exists(path) and os.path.exists(mappath): - print "using %s.img from target-files" % (which,) + print("using %s.img from target-files" % which) # This is a 'new' target-files, which already has the image in it. else: - print "building %s.img from target-files" % (which,) + print("building %s.img from target-files" % which) # This is an 'old' target-files, which does not contain images # already built. Build them. @@ -496,6 +514,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 +540,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 +565,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 +617,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, 0o755, 0o644, None, None) + script.SetPermissionsRecursive("/tmp/install/bin", 0, 0, 0o755, 0o755, 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 +637,15 @@ else if get_stage("%(bcb_dev)s") == "3/3" then if HasVendorPartition(input_zip): system_progress -= 0.1 + if not OPTIONS.wipe_user_data: + 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,15 +750,19 @@ 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 - for kv in sorted(metadata.iteritems())])) + for kv in sorted(metadata.items())])) def LoadPartitionFiles(z, partition): @@ -744,18 +810,24 @@ 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, source_version=source_version, target_zip=target_zip, + input_zip=target_zip, target_version=target_version, + input_version=target_version, output_zip=output_zip, script=script, metadata=metadata, @@ -767,10 +839,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", @@ -901,8 +974,8 @@ else if get_stage("%(bcb_dev)s") != "3/3" then else: include_full_boot = False - print "boot target: %d source: %d diff: %d" % ( - target_boot.size, source_boot.size, len(d)) + print("boot target: %d source: %d diff: %d" % ( + target_boot.size, source_boot.size, len(d))) common.ZipWriteStr(output_zip, "patch/boot.img.p", d) @@ -938,19 +1011,19 @@ else if OPTIONS.two_step: common.ZipWriteStr(output_zip, "boot.img", target_boot.data) script.WriteRawImage("/boot", "boot.img") - print "writing full boot image (forced by two-step mode)" + print("writing full boot image (forced by two-step mode)") if not OPTIONS.two_step: if updating_boot: if include_full_boot: - print "boot image changed; including full." + print("boot image changed; including full.") script.Print("Installing boot image...") script.WriteRawImage("/boot", "boot.img") else: # Produce the boot image by applying a patch to the current # contents of the boot partition, and write it back to the # partition. - print "boot image changed; including patch." + print("boot image changed; including patch.") script.Print("Patching boot image...") script.ShowProgress(0.1, 10) script.ApplyPatch("%s:%s:%d:%s:%d:%s" @@ -961,7 +1034,7 @@ else target_boot.size, target_boot.sha1, source_boot.sha1, "patch/boot.img.p") else: - print "boot image unchanged; skipping." + print("boot image unchanged; skipping.") # Do device-specific installation (eg, write radio image). device_specific.IncrementalOTA_InstallEnd() @@ -988,9 +1061,9 @@ endif; class FileDifference(object): def __init__(self, partition, source_zip, target_zip, output_zip): self.deferred_patch_list = None - print "Loading target..." + print("Loading target...") self.target_data = target_data = LoadPartitionFiles(target_zip, partition) - print "Loading source..." + print("Loading source...") self.source_data = source_data = LoadPartitionFiles(source_zip, partition) self.verbatim_targets = verbatim_targets = [] @@ -1017,14 +1090,14 @@ class FileDifference(object): assert fn == tf.name sf = ClosestFileMatch(tf, matching_file_cache, renames) if sf is not None and sf.name != tf.name: - print "File has moved from " + sf.name + " to " + tf.name + print("File has moved from " + sf.name + " to " + tf.name) renames[sf.name] = tf if sf is None or fn in OPTIONS.require_verbatim: # This file should be included verbatim if fn in OPTIONS.prohibit_verbatim: raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,)) - print "send", fn, "verbatim" + print("send", fn, "verbatim") tf.AddToZip(output_zip) verbatim_targets.append((fn, tf.size, tf.sha1)) if fn in target_data.keys(): @@ -1109,8 +1182,8 @@ class FileDifference(object): def EmitRenames(self, script): if len(self.renames) > 0: script.Print("Renaming files...") - for src, tgt in self.renames.iteritems(): - print "Renaming " + src + " to " + tgt.name + for src, tgt in self.renames.items(): + print("Renaming " + src + " to " + tgt.name) script.RenameFile(src, tgt.name) @@ -1144,18 +1217,24 @@ 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, source_version=source_version, target_zip=target_zip, + input_zip=target_zip, target_version=target_version, + input_version=target_version, output_zip=output_zip, script=script, metadata=metadata, @@ -1169,20 +1248,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", @@ -1250,8 +1328,8 @@ else if get_stage("%(bcb_dev)s") != "3/3" then """ % bcb_dev) # Dump fingerprints - script.Print("Source: %s" % (source_fp,)) - script.Print("Target: %s" % (target_fp,)) + script.Print("Source: %s" % source_fp) + script.Print("Target: %s" % target_fp) script.Print("Verifying current system...") @@ -1265,8 +1343,8 @@ else if get_stage("%(bcb_dev)s") != "3/3" then if updating_boot: d = common.Difference(target_boot, source_boot) _, _, d = d.ComputePatch() - print "boot target: %d source: %d diff: %d" % ( - target_boot.size, source_boot.size, len(d)) + print("boot target: %d source: %d diff: %d" % ( + target_boot.size, source_boot.size, len(d))) common.ZipWriteStr(output_zip, "patch/boot.img.p", d) @@ -1305,7 +1383,7 @@ else if OPTIONS.two_step: common.ZipWriteStr(output_zip, "boot.img", target_boot.data) script.WriteRawImage("/boot", "boot.img") - print "writing full boot image (forced by two-step mode)" + print("writing full boot image (forced by two-step mode)") script.Print("Removing unneeded files...") system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",)) @@ -1340,9 +1418,9 @@ else source_boot.sha1, "patch/boot.img.p") so_far += target_boot.size script.SetProgress(so_far / total_patch_size) - print "boot image changed; including." + print("boot image changed; including.") else: - print "boot image unchanged; skipping." + print("boot image unchanged; skipping.") system_items = ItemSet("system", "META/filesystem_config.txt") if vendor_diff: @@ -1367,9 +1445,9 @@ else target_recovery, target_boot) script.DeleteFiles(["/system/recovery-from-boot.p", "/system/etc/install-recovery.sh"]) - print "recovery image changed; including as patch from boot." + print("recovery image changed; including as patch from boot.") else: - print "recovery image unchanged; skipping." + print("recovery image unchanged; skipping.") script.ShowProgress(0.1, 10) @@ -1499,7 +1577,6 @@ endif; WriteMetadata(metadata, output_zip) - def main(argv): def option_handler(o, a): @@ -1540,7 +1617,7 @@ def main(argv): OPTIONS.verify = True elif o == "--block": OPTIONS.block_based = True - elif o in ("-b", "--binary"): + elif o in ("-b", "--binary",): OPTIONS.updater_binary = a elif o in ("--no_fallback_to_full",): OPTIONS.fallback_to_full = False @@ -1550,6 +1627,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 +1658,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: @@ -1584,7 +1670,7 @@ def main(argv): if OPTIONS.extra_script is not None: OPTIONS.extra_script = open(OPTIONS.extra_script).read() - print "unzipping target target-files..." + print("unzipping target target-files...") OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) OPTIONS.target_tmp = OPTIONS.input_tmp @@ -1599,7 +1685,7 @@ def main(argv): OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts") if OPTIONS.verbose: - print "--- target info ---" + print("--- target info ---") common.DumpInfoDict(OPTIONS.info_dict) # If the caller explicitly specified the device-specific extensions @@ -1612,7 +1698,7 @@ def main(argv): if OPTIONS.device_specific is None: from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") if os.path.exists(from_input): - print "(using device-specific extensions from target_files)" + print("(using device-specific extensions from target_files)") OPTIONS.device_specific = from_input else: OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None) @@ -1634,7 +1720,7 @@ def main(argv): cache_size = OPTIONS.info_dict.get("cache_size", None) if cache_size is None: - raise RuntimeError("can't determine the cache partition size") + print("--- can't determine the cache partition size ---") OPTIONS.cache_size = cache_size if OPTIONS.incremental_source is None: @@ -1647,7 +1733,7 @@ def main(argv): break else: - print "unzipping source target-files..." + print("unzipping source target-files...") OPTIONS.source_tmp, source_zip = common.UnzipTemp( OPTIONS.incremental_source) OPTIONS.target_info_dict = OPTIONS.info_dict @@ -1660,7 +1746,7 @@ def main(argv): "default_system_dev_certificate", "build/target/product/security/testkey") if OPTIONS.verbose: - print "--- source info ---" + print("--- source info ---") common.DumpInfoDict(OPTIONS.source_info_dict) try: WriteIncrementalOTAPackage(input_zip, source_zip, output_zip) @@ -1669,7 +1755,7 @@ def main(argv): except ValueError: if not OPTIONS.fallback_to_full: raise - print "--- failed to build incremental; falling back to full ---" + print("--- failed to build incremental; falling back to full ---") OPTIONS.incremental_source = None common.ZipClose(output_zip) @@ -1677,7 +1763,7 @@ def main(argv): SignOutput(temp_zip_file.name, args[1]) temp_zip_file.close() - print "done." + print("done.") if __name__ == '__main__': @@ -1685,9 +1771,9 @@ if __name__ == '__main__': common.CloseInheritedPipes() main(sys.argv[1:]) except common.ExternalError as e: - print - print " ERROR: %s" % (e,) - print + print() + print(" ERROR: %s" % e) + print() sys.exit(1) finally: common.Cleanup() diff --git a/tools/releasetools/rangelib.py b/tools/releasetools/rangelib.py index 373bbed..cbb34b9 100644 --- a/tools/releasetools/rangelib.py +++ b/tools/releasetools/rangelib.py @@ -43,6 +43,8 @@ class RangeSet(object): return self.data != other.data def __nonzero__(self): return bool(self.data) + def __bool__(self): + return self.__nonzero__() def __str__(self): if not self.data: diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py index 60d62c2..abdb845 100755 --- a/tools/releasetools/sign_target_files_apks.py +++ b/tools/releasetools/sign_target_files_apks.py @@ -65,14 +65,15 @@ Usage: sign_target_files_apks [flags] input_target_files output_target_files """ +from __future__ import print_function + import sys if sys.hexversion < 0x02070000: - print >> sys.stderr, "Python 2.7 or newer is required." + print("Python 2.7 or newer is required.", file=sys.stderr) sys.exit(1) import base64 -import cStringIO import copy import errno import os @@ -82,6 +83,11 @@ import subprocess import tempfile import zipfile +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO + import add_img_to_target_files import common @@ -98,11 +104,11 @@ def GetApkCerts(tf_zip): certmap = common.ReadApkCerts(tf_zip) # apply the key remapping to the contents of the file - for apk, cert in certmap.iteritems(): + for apk, cert in certmap.items(): certmap[apk] = OPTIONS.key_map.get(cert, cert) # apply all the -e options, overriding anything in the file - for apk, cert in OPTIONS.extra_apks.iteritems(): + for apk, cert in OPTIONS.extra_apks.items(): if not cert: cert = "PRESIGNED" certmap[apk] = OPTIONS.key_map.get(cert, cert) @@ -120,10 +126,10 @@ def CheckAllApksSigned(input_tf_zip, apk_key_map): if name not in apk_key_map: unknown_apks.append(name) if unknown_apks: - print "ERROR: no key specified for:\n\n ", - print "\n ".join(unknown_apks) - print "\nUse '-e <apkname>=' to specify a key (which may be an" - print "empty string to not sign this apk)." + print("ERROR: no key specified for:\n\n ", end=' ') + print("\n ".join(unknown_apks)) + print("\nUse '-e <apkname>=' to specify a key (which may be an") + print("empty string to not sign this apk).") sys.exit(1) @@ -172,6 +178,9 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, if info.filename.startswith("IMAGES/"): continue + if info.filename.startswith("BOOTABLE_IMAGES/"): + continue + data = input_tf_zip.read(info.filename) out_info = copy.copy(info) @@ -194,25 +203,29 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, name = os.path.basename(info.filename) key = apk_key_map[name] if key not in common.SPECIAL_CERT_STRINGS: - print " signing: %-*s (%s)" % (maxsize, name, key) + print(" signing: %-*s (%s)" % (maxsize, name, key)) signed_data = SignApk(data, key, key_passwords[key]) common.ZipWriteStr(output_tf_zip, out_info, signed_data) else: # an APK we're not supposed to sign. - print "NOT signing: %s" % (name,) + print("NOT signing: %s" % name) common.ZipWriteStr(output_tf_zip, out_info, data) elif info.filename in ("SYSTEM/build.prop", "VENDOR/build.prop", "BOOT/RAMDISK/default.prop", "RECOVERY/RAMDISK/default.prop"): - print "rewriting %s:" % (info.filename,) + print("rewriting %s:" % info.filename) new_data = RewriteProps(data, misc_info) common.ZipWriteStr(output_tf_zip, out_info, new_data) if info.filename in ("BOOT/RAMDISK/default.prop", "RECOVERY/RAMDISK/default.prop"): write_to_temp(info.filename, info.external_attr, new_data) elif info.filename.endswith("mac_permissions.xml"): - print "rewriting %s with new keys." % (info.filename,) + print("rewriting %s with new keys." % info.filename) + new_data = ReplaceCerts(data) + common.ZipWriteStr(output_tf_zip, out_info, new_data) + elif info.filename.startswith("SYSTEM/etc/permissions/"): + print("rewriting %s with new keys." % info.filename) new_data = ReplaceCerts(data) common.ZipWriteStr(output_tf_zip, out_info, new_data) elif info.filename in ("SYSTEM/recovery-from-boot.p", @@ -257,10 +270,10 @@ def ReplaceCerts(data): """Given a string of data, replace all occurences of a set of X509 certs with a newer set of X509 certs and return the updated data string.""" - for old, new in OPTIONS.key_map.iteritems(): + for old, new in OPTIONS.key_map.items(): try: if OPTIONS.verbose: - print " Replacing %s.x509.pem with %s.x509.pem" % (old, new) + print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new)) f = open(old + ".x509.pem") old_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower() f.close() @@ -271,14 +284,14 @@ def ReplaceCerts(data): pattern = "\\b"+old_cert16+"\\b" (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE) if OPTIONS.verbose: - print " Replaced %d occurence(s) of %s.x509.pem with " \ - "%s.x509.pem" % (num, old, new) + print(" Replaced %d occurence(s) of %s.x509.pem with " + "%s.x509.pem" % (num, old, new)) except IOError as e: if e.errno == errno.ENOENT and not OPTIONS.verbose: continue - print " Error accessing %s. %s. Skip replacing %s.x509.pem " \ - "with %s.x509.pem." % (e.filename, e.strerror, old, new) + print(" Error accessing %s. %s. Skip replacing %s.x509.pem " + "with %s.x509.pem." % (e.filename, e.strerror, old, new)) return data @@ -331,8 +344,8 @@ def RewriteProps(data, misc_info): value = " ".join(value) line = key + "=" + value if line != original_line: - print " replace: ", original_line - print " with: ", line + print(" replace: ", original_line) + print(" with: ", line) output.append(line) return "\n".join(output) + "\n" @@ -348,7 +361,7 @@ def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem" for k in extra_recovery_keys.split()] if extra_recovery_keys: - print "extra recovery-only key(s): " + ", ".join(extra_recovery_keys) + print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys)) else: extra_recovery_keys = [] @@ -362,14 +375,14 @@ def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") if mapped_keys: - print "using:\n ", "\n ".join(mapped_keys) - print "for OTA package verification" + print("using:\n ", "\n ".join(mapped_keys)) + print("for OTA package verification") else: devkey = misc_info.get("default_system_dev_certificate", "build/target/product/security/testkey") mapped_keys.append( OPTIONS.key_map.get(devkey, devkey) + ".x509.pem") - print "META/otakeys.txt has no keys; using", mapped_keys[0] + print("META/otakeys.txt has no keys; using", mapped_keys[0]) # recovery uses a version of the key that has been slightly # predigested (by DumpPublicKey.java) and put in res/keys. @@ -385,11 +398,19 @@ def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys", new_recovery_keys) + # Save the base64 key representation in the update for key-change + # validations + p = common.Run(["python", "build/tools/getb64key.py", mapped_keys[0]], + stdout=subprocess.PIPE) + data, _ = p.communicate() + if p.returncode == 0: + common.ZipWriteStr(output_tf_zip, "META/releasekey.txt", data) + # SystemUpdateActivity uses the x509.pem version of the keys, but # put into a zipfile system/etc/security/otacerts.zip. # We DO NOT include the extra_recovery_keys (if any) here. - temp_file = cStringIO.StringIO() + temp_file = StringIO() certs_zip = zipfile.ZipFile(temp_file, "w") for k in mapped_keys: certs_zip.write(k) @@ -400,7 +421,7 @@ def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): return new_recovery_keys def ReplaceVerityPublicKey(targetfile_zip, key_path): - print "Replacing verity public key with %s" % key_path + print("Replacing verity public key with %s" % key_path) with open(key_path) as f: data = f.read() common.ZipWriteStr(targetfile_zip, "BOOT/RAMDISK/verity_key", data) @@ -408,7 +429,7 @@ def ReplaceVerityPublicKey(targetfile_zip, key_path): def ReplaceVerityPrivateKey(targetfile_input_zip, targetfile_output_zip, misc_info, key_path): - print "Replacing verity private key with %s" % key_path + print("Replacing verity private key with %s" % key_path) current_key = misc_info["verity_key"] original_misc_info = targetfile_input_zip.read("META/misc_info.txt") new_misc_info = original_misc_info.replace(current_key, key_path) @@ -499,14 +520,14 @@ def main(argv): add_img_to_target_files.AddImagesToTargetFiles(args[1]) - print "done." + print("done.") if __name__ == '__main__': try: main(sys.argv[1:]) - except common.ExternalError, e: - print - print " ERROR: %s" % (e,) - print + except common.ExternalError as e: + print() + print(" ERROR: %s" % e) + print() sys.exit(1) diff --git a/tools/releasetools/sparse_img.py b/tools/releasetools/sparse_img.py index 013044f..fa4406c 100644 --- a/tools/releasetools/sparse_img.py +++ b/tools/releasetools/sparse_img.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function + import bisect import os import struct diff --git a/tools/repopick.py b/tools/repopick.py new file mode 100755 index 0000000..1840a56 --- /dev/null +++ b/tools/repopick.py @@ -0,0 +1,371 @@ +#!/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]) + if not hasattr(out, 'encode'): + out = out.decode() + 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': '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['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]') + parser.add_argument('-e', '--exclude', nargs=1, help='exclude a list of commit numbers separated by a ,') + 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']) + if not hasattr(plist, 'encode'): + plist = plist.decode() + needs_abandon = False + for pline in plist.splitlines(): + 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 = [] + + # If --exclude is given, create the list of commits to ignore + exclude = [] + if args.exclude: + exclude = args.exclude[0].split(',') + + for change in change_numbers: + patchset = None + if '/' in change: + (change, patchset) = change.split('/') + + if change in exclude: + continue + + change = int(change) + review = next((x for x in reviews if x['number'] == change), None) + if review is None: + print('Change %d not found, skipping' % change) + continue + + 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) + FETCH_HEAD = '{0}/.git/FETCH_HEAD'.format(project_path) + if result != 0 and os.stat(FETCH_HEAD).st_size != 0: + print('ERROR: git command failed') + sys.exit(result) + # 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..a1b69cd --- /dev/null +++ b/tools/roomservice.py @@ -0,0 +1,297 @@ +#!/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: + auth_string = ('%s:%s' % (authtuple[0], authtuple[2])).encode() + githubauth = base64.encodestring(auth_string).decode().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, path): + for child in lm.getchildren(): + if child.attrib['path'] == path: + 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(projectpath): + 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("path") == projectpath: + return True + + ## 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("path") == projectpath: + return True + + return False + +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'] + print('Checking if %s is fetched from %s' % (repo_target, repo_name)) + if is_in_manifest(repo_target): + print('CyanogenMod/%s already fetched to %s' % (repo_name, repo_target)) + 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(dependency['target_path']): + 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) diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java index 88f486a..3ddab11 100644 --- a/tools/signapk/SignApk.java +++ b/tools/signapk/SignApk.java @@ -167,18 +167,29 @@ class SignApk { } /** - * Reads the password from console and returns it as a string. + * If a console doesn't exist, reads the password from stdin + * If a console exists, reads the password from console and returns it as a string. * * @param keyFile The file containing the private key. Used to prompt the user. */ private static String readPassword(File keyFile) { Console console; char[] pwd; - if((console = System.console()) != null && - (pwd = console.readPassword("[%s]", "Enter password for " + keyFile)) != null){ - return String.valueOf(pwd); + if ((console = System.console()) == null) { + System.out.print("Enter password for " + keyFile + " (password will not be hidden): "); + System.out.flush(); + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + try { + return stdin.readLine(); + } catch (IOException ex) { + return null; + } } else { - return null; + if ((pwd = console.readPassword("[%s]", "Enter password for " + keyFile)) != null) { + return String.valueOf(pwd); + } else { + return null; + } } } diff --git a/tools/warn.py b/tools/warn.py index 8097123..b5a49f6 100755 --- a/tools/warn.py +++ b/tools/warn.py @@ -1,11 +1,13 @@ #!/usr/bin/env python # This file uses the following encoding: utf-8 +from __future__ import print_function + import sys import re if len(sys.argv) == 1: - print 'usage: ' + sys.argv[0] + ' <build.log>' + print('usage: ' + sys.argv[0] + ' <build.log>') sys.exit() # if you add another level, don't forget to give it a color below @@ -399,7 +401,7 @@ cur_row_color = 0 row_colors = [ 'e0e0e0', 'd0d0d0' ] def output(text): - print text, + print(text, end=' ') def htmlbig(param): return '<font size="+2">' + param + '</font>' |