1#!/bin/bash 2# 3# Generates an SDK Repository XML based on the input files. 4 5set -e 6 7PROG_DIR=$(dirname $0) 8 9TYPES="tool platform-tool platform sample doc add-on system-image source support" 10OSES="linux macosx windows any linux-x86 darwin" 11 12TMP_DIR=$(mktemp -d -t sdkrepo.tmp.XXXXXXXX) 13trap "rm -rf $TMP_DIR" EXIT 14 15function debug() { 16 echo "DEBUG: " $@ > /dev/stderr 17} 18 19function error() { 20 echo "*** ERROR: " $@ 21 usage 22} 23 24function usage() { 25 cat <<EOFU 26Usage: $0 output.xml xml-schema [type [os zip[:dest]]*...]* 27where: 28- schema is one of 'repository' or 'addon' 29- type is one of ${TYPES// /, } (or their plural). 30- os is one of ${OSES// /, }. 31There can be more than one zip for the same type 32as long as they use different OSes. 33Zip can be in the form "source:dest" to be renamed on the fly. 34EOFU 35 exit 1 36} 37 38# Validate the tools we need 39if [[ ! -x $(which sha1sum) ]]; then 40 error "Missing tool: sha1sum (Linux: apt-get install coreutils; Mac: port install md5sha1sum)" 41fi 42 43# Parse input params 44OUT="$1" 45[[ -z "$OUT" ]] && error "Missing output.xml name." 46shift 47 48# Get the schema type. Must be either "repository" or "addon". 49SCHEMA="$1" 50[[ ! -f "$SCHEMA" ]] && error "Invalid XML schema name: $SCHEMA." 51shift 52 53# Get XML:NS for SDK from the schema 54# This will be something like "http://schemas.android.com/sdk/android/addon/3" 55XMLNS=$(sed -n '/xmlns:sdk="/s/.*"\(.*\)".*/\1/p' "$SCHEMA") 56[[ -z "$XMLNS" ]] && error "Failed to find xmlns:sdk in $SCHEMA." 57echo "## Using xmlns:sdk=$XMLNS" 58 59# Extract the schema version number from the XMLNS, e.g. it would extract "3" 60VERSION="${XMLNS##*/}" 61 62# Get the root element from the schema. This is the first element 63# which name starts with "sdk-" (e.g. sdk-repository, sdk-addon) 64ROOT=$(sed -n -e '/xsd:element.*name="sdk-/s/.*name="\(sdk-[^"]*\)".*/\1/p' "$SCHEMA") 65[[ -z "$ROOT" ]] && error "Failed to find root element in $SCHEMA." 66echo "## Using root element $ROOT" 67 68# Generate XML header 69cat > "$OUT" <<EOFH 70<?xml version="1.0"?> 71<sdk:$ROOT 72 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 73 xmlns:sdk="$XMLNS"> 74EOFH 75 76# check_enum value value1 value2 value3... 77# returns valueN if matched or nothing. 78function check_enum() { 79 local VALUE="$1" 80 local i 81 shift 82 for i in "$@"; do 83 if [[ "$i" == "$VALUE" ]]; then 84 echo "$VALUE" 85 break; 86 fi 87 done 88} 89 90# Parse all archives. 91 92ATTRS=( 93 # Columns: 94 # --------------------------+------------------------+---------------------- 95 # Name read from | XML element written | Min-XSD version 96 # source.properties | to repository.xml | where XML can be used 97 # --------------------------+------------------------+---------------------- 98 # from source.properties for repository.xml packages 99 Pkg.Revision revision 1 100 Pkg.Desc description 1 101 Platform.Version version 1 102 AndroidVersion.ApiLevel api-level 1 103 AndroidVersion.CodeName codename 1 104 Platform.IncludedAbi included-abi 5 105 Platform.MinToolsRev min-tools-rev 1 106 Platform.MinPlatformToolsRev min-platform-tools-rev 3 107 Sample.MinApiLevel min-api-level 2 108 SystemImage.Abi abi 5 109 Layoutlib.Api layoutlib/api 4 110 Layoutlib.Revision layoutlib/revision 4 111 # from source.properties for addon.xml packages 112 # (note that vendor is mapped to different XML elements based on the XSD version) 113 Extra.VendorDisplay vendor-display 4 114 Extra.VendorId vendor-id 4 115 Extra.Vendor vendor-id 4 116 Extra.Vendor vendor 1 117 Extra.NameDisplay name-display 4 118 Extra.Path path 1 119 Extra.OldPaths old-paths 3 120 Extra.MinApiLevel min-api-level 2 121 # from addon manifest.ini for addon.xml packages 122 # (note that vendor/name are mapped to different XML elements based on the XSD version) 123 vendor-id vendor-id 4 124 vendor-display vendor-display 4 125 vendor vendor-display 4 126 vendor vendor 1 127 name-id name-id 4 128 name-display name-display 4 129 name name-display 4 130 name name 1 131 description description 1 132 api api-level 1 133 version revision 1 134 revision revision 1 135) 136 137function parse_attributes() { 138 local PROPS="$1" 139 shift 140 local RESULT="" 141 local VALUE 142 local REV 143 local USED 144 145 # $1 here is the ATTRS list above. 146 while [[ "$1" ]]; do 147 # Check the version in which the attribute was introduced and 148 # ignore things which are too *new* for this schema. This lets 149 # us generate old schemas for backward compatibility purposes. 150 SRC=$1 151 DST=$2 152 REV=$3 153 154 if [[ $VERSION -ge $REV ]]; then 155 # Parse the property, if present. Any space is replaced by @ 156 VALUE=$( grep "^$SRC=" "$PROPS" | cut -d = -f 2 | tr ' ' '@' | tr -d '\r' ) 157 if [[ -n "$VALUE" ]]; then 158 # In case an XML element would be mapped multiple times, 159 # only use its first definition. 160 if [[ "${USED/$DST/}" == "$USED" ]]; then 161 USED="$USED $DST" 162 RESULT="$RESULT $DST $VALUE" 163 fi 164 fi 165 fi 166 shift 167 shift 168 shift 169 done 170 171 echo "$RESULT" 172} 173 174function output_attributes() { 175 local OUT="$1" 176 shift 177 local KEY VALUE 178 local NODE LAST_NODE EXTRA_SPACE 179 180 while [[ "$1" ]]; do 181 KEY="$1" 182 VALUE="${2//@/ }" 183 NODE="${KEY%%/*}" 184 KEY="${KEY##*/}" 185 if [[ "$NODE" == "$KEY" ]]; then 186 NODE="" 187 EXTRA_SPACE="" 188 fi 189 if [[ "$NODE" != "$LAST_NODE" ]]; then 190 EXTRA_SPACE=" " 191 [[ "$LAST_NODE" ]] && echo " </sdk:$LAST_NODE>" >> "$OUT" 192 LAST_NODE="$NODE" 193 [[ "$NODE" ]] && echo " <sdk:$NODE>" >> "$OUT" 194 fi 195 echo "$EXTRA_SPACE <sdk:$KEY>$VALUE</sdk:$KEY>" >> "$OUT" 196 shift 197 shift 198 done 199 if [[ "$LAST_NODE" ]]; then echo " </sdk:$LAST_NODE>" >> "$OUT"; fi 200} 201 202while [[ -n "$1" ]]; do 203 # Process archives. 204 # First we expect a type. For convenience the type can be plural. 205 TYPE=$(check_enum "${1%%s}" $TYPES) 206 [[ -z $TYPE ]] && error "Unknown archive type '$1'." 207 shift 208 209 ELEMENT="$TYPE" 210 # The element name is different for extras: 211 [[ "$TYPE" == "support" ]] && ELEMENT="extra" 212 213 MAP="" 214 FIRST="1" 215 LIBS_XML="" 216 217 OS=$(check_enum "$1" $OSES) 218 while [[ $OS ]]; do 219 shift 220 [[ $OS == "linux-x86" ]] && OS=linux 221 [[ $OS == "darwin" ]] && OS=macosx 222 223 SRC="$1" 224 DST="$1" 225 if [[ "${SRC/:/}" != "$SRC" ]]; then 226 DST="${SRC/*:/}" 227 SRC="${SRC/:*/}" 228 fi 229 [[ ! -f "$SRC" ]] && error "Missing file for archive $TYPE/$OS: $SRC" 230 shift 231 232 # Depending on the archive type, we need a number of attributes 233 # from the source.properties or the manifest.ini. We'll take 234 # these attributes from the first zip found. 235 # 236 # What we need vs. which package uses it: 237 # - description all 238 # - revision all 239 # - version platform 240 # - included-abi platform 241 # - api-level platform sample doc add-on system-image 242 # - codename platform sample doc add-on system-image 243 # - min-tools-rev platform sample 244 # - min-platform-tools-rev tool 245 # - min-api-level extra 246 # - vendor extra add-on 247 # - path extra 248 # - old-paths extra 249 # - abi system-image 250 # 251 # We don't actually validate here. 252 # Just take whatever is defined and put it in the XML. 253 # XML validation against the schema will be done at the end. 254 255 if [[ $FIRST ]]; then 256 FIRST="" 257 258 if unzip -t "$SRC" | grep -qs "source.properties" ; then 259 # Extract Source Properties 260 # unzip: -j=flat (no dirs), -q=quiet, -o=overwrite, -d=dest dir 261 unzip -j -q -o -d "$TMP_DIR" "$SRC" "*/source.properties" 262 PROPS="$TMP_DIR/source.properties" 263 264 elif unzip -t "$SRC" | grep -qs "manifest.ini" ; then 265 unzip -j -q -o -d "$TMP_DIR" "$SRC" "*/manifest.ini" 266 PROPS="$TMP_DIR/manifest.ini" 267 268 # Parse the libs for an addon and generate the <libs> node 269 # libraries is a semi-colon separated list 270 LIBS=$(parse_attributes "$PROPS" "libraries") 271 LIBS_XML=" <sdk:libs>" 272 for LIB in ${LIBS//;/ }; do 273 LIBS_XML="$LIBS_XML 274 <sdk:lib><sdk:name>$LIB</sdk:name></sdk:lib>" 275 done 276 LIBS_XML="$LIBS_XML 277 </sdk:libs>" 278 279 else 280 error "Failed to find source.properties or manifest.ini in $SRC" 281 fi 282 283 [[ ! -f $PROPS ]] && error "Failed to extract $PROPS from $SRC" 284 MAP=$(parse_attributes "$PROPS" ${ATTRS[@]}) 285 286 # Time to generate the XML for the package 287 echo " <sdk:${ELEMENT}>" >> "$OUT" 288 output_attributes "$OUT" $MAP 289 [[ -n "$LIBS_XML" ]] && echo "$LIBS_XML" >> "$OUT" 290 echo " <sdk:archives>" >> "$OUT" 291 fi 292 293 # Generate archive info 294 echo "## Add $TYPE/$OS archive $SRC" 295 if [[ $( uname ) == "Darwin" ]]; then 296 SIZE=$( stat -f %z "$SRC" ) 297 else 298 SIZE=$( stat -c %s "$SRC" ) 299 fi 300 SHA1=$( sha1sum "$SRC" | cut -d " " -f 1 ) 301 302 cat >> "$OUT" <<EOFA 303 <sdk:archive os='$OS' arch='any'> 304 <sdk:size>$SIZE</sdk:size> 305 <sdk:checksum type='sha1'>$SHA1</sdk:checksum> 306 <sdk:url>$DST</sdk:url> 307 </sdk:archive> 308EOFA 309 310 # Skip to next arch/zip entry. 311 # If not a valid OS, close the archives/package nodes. 312 OS=$(check_enum "$1" $OSES) 313 314 if [[ ! "$OS" ]]; then 315 echo " </sdk:archives>" >> "$OUT" 316 echo " </sdk:${ELEMENT}>" >> "$OUT" 317 fi 318 done 319 320done 321 322# Generate XML footer 323echo "</sdk:$ROOT>" >> "$OUT" 324 325echo "## Validate XML against schema" 326xmllint --schema $SCHEMA "$OUT" 327