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 build-tool platform sample doc add-on system-image source" 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 43if [[ -z "$XMLLINT" ]]; then 44 XMLLINT=xmllint 45fi 46 47# Parse input params 48OUT="$1" 49[[ -z "$OUT" ]] && error "Missing output.xml name." 50shift 51 52# Get the schema filename. E.g. ".../.../sdk-repository-10.xsd". Can be relative or absolute. 53SCHEMA="$1" 54[[ ! -f "$SCHEMA" ]] && error "Invalid XML schema name: $SCHEMA." 55shift 56 57# Get XML:NS for SDK from the schema 58# This will be something like "http://schemas.android.com/sdk/android/addon/3" 59XMLNS=$(sed -n '/xmlns:sdk="/s/.*"\(.*\)".*/\1/p' "$SCHEMA") 60[[ -z "$XMLNS" ]] && error "Failed to find xmlns:sdk in $SCHEMA." 61#echo "## Using xmlns:sdk=$XMLNS" 62 63# Extract the schema version number from the XMLNS, e.g. it would extract "3" 64XSD_VERSION="${XMLNS##*/}" 65 66# Get the root element from the schema. This is the first element 67# which name starts with "sdk-" (e.g. sdk-repository, sdk-addon) 68ROOT=$(sed -n -e '/xsd:element.*name="sdk-/s/.*name="\(sdk-[^"]*\)".*/\1/p' "$SCHEMA") 69[[ -z "$ROOT" ]] && error "Failed to find root element in $SCHEMA." 70#echo "## Using root element $ROOT" 71 72# Generate XML header 73cat > "$OUT" <<EOFH 74<?xml version="1.0"?> 75<sdk:$ROOT 76 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 77 xmlns:sdk="$XMLNS"> 78EOFH 79 80# check_enum value value1 value2 value3... 81# returns valueN if matched or nothing. 82function check_enum() { 83 local VALUE="$1" 84 local i 85 shift 86 for i in "$@"; do 87 if [[ "$i" == "$VALUE" ]]; then 88 echo "$VALUE" 89 break; 90 fi 91 done 92} 93 94# Definition of the attributes we read from source.properties or manifest.ini 95# files and the equivalent XML element being generated. 96 97ATTRS=( 98 # Columns: 99 # --------------------------+------------------------+---------------------- 100 # Name read from | XML element written | Min-XSD version 101 # source.properties | to repository.xml | where XML can be used 102 # --------------------------+------------------------+---------------------- 103 # from source.properties for repository.xml packages 104 Pkg.Revision revision 1 105 Pkg.Desc description 1 106 Platform.Version version 1 107 AndroidVersion.ApiLevel api-level 1 108 AndroidVersion.CodeName codename 1 109 Platform.IncludedAbi included-abi 5 110 Platform.MinToolsRev min-tools-rev 1 111 Platform.MinPlatformToolsRev min-platform-tools-rev 3 112 Sample.MinApiLevel min-api-level 2 113 Layoutlib.Api layoutlib/api 4 114 Layoutlib.Revision layoutlib/revision 4 115 # from source.properties for addon.xml packages 116 # (note that vendor is mapped to different XML elements based on the XSD version) 117 Extra.VendorDisplay vendor-display 4 118 Extra.VendorId vendor-id 4 119 Extra.Vendor vendor-id 4 120 Extra.Vendor vendor 1 121 Extra.NameDisplay name-display 4 122 Extra.Path path 1 123 Extra.OldPaths old-paths 3 124 Extra.MinApiLevel min-api-level 2 125 # for system-image 126 SystemImage.Abi abi r:3,s:1 127 SystemImage.TagId tag-id r:9,s:2 128 SystemImage.TagDisplay tag-display r:9,s:2 129 Addon.VendorId add-on/vendor-id s:3 130 Addon.VendorDisplay add-on/vendor-display s:3 131 # from addon manifest.ini for addon.xml packages 132 # (note that vendor/name are mapped to different XML elements based on the XSD version) 133 vendor-id vendor-id 4 134 vendor-display vendor-display 4 135 vendor vendor-display 4 136 vendor vendor 1 137 name-id name-id 4 138 name-display name-display 4 139 name name-display 4 140 name name 1 141 description description 1 142 api api-level 1 143 version revision 1 144 revision revision 1 145) 146 147# Start with repo-10, addon-7 and sys-img-3, we don't encode the os/arch 148# in the <archive> attributes anymore. Instead we have separate elements. 149 150function uses_new_host_os() { 151 if [[ "$ROOT" == "sdk-repository" && "$XSD_VERSION" -ge "10" ]]; then return 0; fi 152 if [[ "$ROOT" == "sdk-addon" && "$XSD_VERSION" -ge "7" ]]; then return 0; fi 153 if [[ "$ROOT" == "sdk-sys-img" && "$XSD_VERSION" -ge "3" ]]; then return 0; fi 154 return 1 155} 156 157ATTRS_ARCHIVE=( 158 Archive.HostOs host-os 1 159 Archive.HostBits host-bits 1 160 Archive.JvmBits jvm-bits 1 161 Archive.MinJvmVers min-jvm-version 1 162) 163 164 165# Starting with XSD repo-7 and addon-5, some revision elements are no longer just 166# integers. Instead they are in major.minor.micro.preview format. This defines 167# which elements. This depends on the XSD root element and the XSD version. 168# 169# Note: addon extra revision can't take a preview number. We don't enforce 170# this in this script. Instead schema validation will fail if the extra 171# source.property declares an RC and it gets inserted in the addon.xml here. 172 173if [[ "$ROOT" == "sdk-repository" && "$XSD_VERSION" -ge 7 ]] || 174 [[ "$ROOT" == "sdk-addon" && "$XSD_VERSION" -ge 5 ]]; then 175FULL_REVISIONS=( 176 tool revision 177 build-tool revision 178 platform-tool revision 179 extra revision 180 @ min-tools-rev 181 @ min-platform-tools-rev 182) 183else 184FULL_REVISIONS=() 185fi 186 187 188# Parse all archives. 189 190function needs_full_revision() { 191 local PARENT="$1" 192 local ELEMENT="$2" 193 shift 194 shift 195 local P E 196 197 while [[ "$1" ]]; do 198 P=$1 199 E=$2 200 if [[ "$E" == "$ELEMENT" ]] && [[ "$P" == "@" || "$P" == "$PARENT" ]]; then 201 return 0 # true 202 fi 203 shift 204 shift 205 done 206 207 return 1 # false 208} 209 210# Parses and print a full revision in the form "1.2.3 rc4". 211# Note that the format requires to have 1 space before the 212# optional "rc" (e.g. '1 rc4', not '1rc4') and no space after 213# the rc (so not '1 rc 4' either) 214function write_full_revision() { 215 local VALUE="$1" 216 local EXTRA_SPACE="$2" 217 local KEYS="major minor micro preview" 218 local V K 219 220 while [[ -n "$VALUE" && -n "$KEYS" ]]; do 221 # Take 1st segment delimited by . or space 222 V="${VALUE%%[. ]*}" 223 224 # Print it 225 if [[ "${V:0:2}" == "rc" ]]; then 226 V="${V:2}" 227 K="preview" 228 KEYS="" 229 else 230 K="${KEYS%% *}" 231 fi 232 233 if [[ -n "$V" && -n "$K" ]]; then 234 echo "$EXTRA_SPACE <sdk:$K>$V</sdk:$K>" 235 fi 236 237 # Take the rest. 238 K="${KEYS#* }" 239 if [[ "$K" == "$KEYS" ]]; then KEYS=""; else KEYS="$K"; fi 240 V="${VALUE#*[. ]}" 241 if [[ "$V" == "$VALUE" ]]; then VALUE=""; else VALUE="$V"; fi 242 done 243} 244 245 246function parse_attributes() { 247 local PROPS="$1" 248 shift 249 local RESULT="" 250 local VALUE 251 local REV 252 local USED 253 local S 254 255 # Get the first letter of the schema name (e.g. sdk-repo => 'r') 256 # This can be r, a or s and would match the min-XSD per-schema value 257 # in the ATTRS list. 258 S=$(basename "$SCHEMA") 259 S="${S:4:1}" 260 261 # $1 here is the ATTRS list above. 262 while [[ "$1" ]]; do 263 # Check the version in which the attribute was introduced and 264 # ignore things which are too *new* for this schema. This lets 265 # us generate old schemas for backward compatibility purposes. 266 SRC=$1 267 DST=$2 268 REV=$3 269 270 if [[ $REV =~ ([ras0-9:,]+,)?$S:([0-9])(,.*)? ]]; then 271 # Per-schema type min-XSD revision. Format is "[<type>:rev],*] 272 # where type is one of r, a or s matching $S above. 273 REV="${BASH_REMATCH[2]}" 274 fi 275 276 if [[ ( $REV =~ ^[0-9]+ && $XSD_VERSION -ge $REV ) || $XSD_VERSION == $REV ]]; then 277 # Parse the property, if present. Any space is replaced by @ 278 VALUE=$( grep "^$SRC=" "$PROPS" | cut -d = -f 2 | tr ' ' '@' | tr -d '\r' ) 279 if [[ -n "$VALUE" ]]; then 280 # In case an XML element would be mapped multiple times, 281 # only use its first definition. 282 if [[ "${USED/$DST/}" == "$USED" ]]; then 283 USED="$USED $DST" 284 RESULT="$RESULT $DST $VALUE" 285 fi 286 fi 287 fi 288 shift 289 shift 290 shift 291 done 292 293 echo "$RESULT" 294} 295 296function output_attributes() { 297 local ELEMENT="$1" 298 local OUT="$2" 299 shift 300 shift 301 local KEY VALUE 302 local NODE LAST_NODE EXTRA_SPACE 303 304 while [[ "$1" ]]; do 305 KEY="$1" 306 VALUE="${2//@/ }" 307 NODE="${KEY%%/*}" 308 KEY="${KEY##*/}" 309 if [[ "$NODE" == "$KEY" ]]; then 310 NODE="" 311 EXTRA_SPACE="" 312 fi 313 if [[ "$NODE" != "$LAST_NODE" ]]; then 314 EXTRA_SPACE=" " 315 [[ "$LAST_NODE" ]] && echo " </sdk:$LAST_NODE>" >> "$OUT" 316 LAST_NODE="$NODE" 317 [[ "$NODE" ]] && echo " <sdk:$NODE>" >> "$OUT" 318 fi 319 if needs_full_revision "$ELEMENT" "$KEY" ${FULL_REVISIONS[@]}; then 320 echo "$EXTRA_SPACE <sdk:$KEY>" >> "$OUT" 321 write_full_revision "$VALUE" "$EXTRA_SPACE" >> "$OUT" 322 echo "$EXTRA_SPACE </sdk:$KEY>" >> "$OUT" 323 else 324 echo "$EXTRA_SPACE <sdk:$KEY>$VALUE</sdk:$KEY>" >> "$OUT" 325 fi 326 shift 327 shift 328 done 329 if [[ "$LAST_NODE" ]]; then echo " </sdk:$LAST_NODE>" >> "$OUT"; fi 330} 331 332while [[ -n "$1" ]]; do 333 # Process archives. 334 # First we expect a type. For convenience the type can be plural. 335 TYPE=$(check_enum "${1%%s}" $TYPES) 336 [[ -z $TYPE ]] && error "Unknown archive type '$1'." 337 shift 338 339 ELEMENT="$TYPE" 340 341 MAP="" 342 FIRST="1" 343 LIBS_XML="" 344 345 OS=$(check_enum "$1" $OSES) 346 while [[ $OS ]]; do 347 shift 348 [[ $OS == "linux-x86" ]] && OS=linux 349 [[ $OS == "darwin" ]] && OS=macosx 350 351 SRC="$1" 352 DST="$1" 353 if [[ "${SRC/:/}" != "$SRC" ]]; then 354 DST="${SRC/*:/}" 355 SRC="${SRC/:*/}" 356 fi 357 [[ ! -f "$SRC" ]] && error "Missing file for archive $TYPE/$OS: $SRC" 358 shift 359 360 # Depending on the archive type, we need a number of attributes 361 # from the source.properties or the manifest.ini. We'll take 362 # these attributes from the first zip found. 363 # 364 # What we need vs. which package uses it: 365 # - description all 366 # - revision all 367 # - version platform 368 # - included-abi platform 369 # - api-level platform sample doc add-on system-image 370 # - codename platform sample doc add-on system-image 371 # - min-tools-rev platform sample 372 # - min-platform-tools-rev tool 373 # - min-api-level extra 374 # - vendor extra add-on 375 # - path extra 376 # - old-paths extra 377 # - abi system-image 378 # 379 # We don't actually validate here. 380 # Just take whatever is defined and put it in the XML. 381 # XML validation against the schema will be done at the end. 382 383 if [[ $FIRST ]]; then 384 FIRST="" 385 386 if unzip -l "$SRC" | grep -qs "source.properties" ; then 387 # Extract Source Properties 388 # unzip: -j=flat (no dirs), -q=quiet, -o=overwrite, -d=dest dir 389 unzip -j -q -o -d "$TMP_DIR" "$SRC" "*/source.properties" 390 PROPS="$TMP_DIR/source.properties" 391 392 elif unzip -l "$SRC" | grep -qs "manifest.ini" ; then 393 unzip -j -q -o -d "$TMP_DIR" "$SRC" "*/manifest.ini" 394 PROPS="$TMP_DIR/manifest.ini" 395 396 # Parse the libs for an addon and generate the <libs> node 397 # libraries is a semi-colon separated list 398 LIBS=$(parse_attributes "$PROPS" "libraries") 399 LIBS_XML=" <sdk:libs>" 400 for LIB in ${LIBS//;/ }; do 401 LIBS_XML="$LIBS_XML 402 <sdk:lib><sdk:name>$LIB</sdk:name></sdk:lib>" 403 done 404 LIBS_XML="$LIBS_XML 405 </sdk:libs>" 406 407 else 408 error "Failed to find source.properties or manifest.ini in $SRC" 409 fi 410 411 [[ ! -f $PROPS ]] && error "Failed to extract $PROPS from $SRC" 412 MAP=$(parse_attributes "$PROPS" ${ATTRS[@]}) 413 414 # Time to generate the XML for the package 415 echo " <sdk:${ELEMENT}>" >> "$OUT" 416 output_attributes "$ELEMENT" "$OUT" $MAP 417 [[ -n "$LIBS_XML" ]] && echo "$LIBS_XML" >> "$OUT" 418 echo " <sdk:archives>" >> "$OUT" 419 fi 420 421 # Generate archive info 422 #echo "## Add $TYPE/$OS archive $SRC" 423 SIZE=$( stat -c %s "$SRC" ) 424 SHA1=$( sha1sum "$SRC" | cut -d " " -f 1 ) 425 426 if uses_new_host_os ; then 427 USE_HOST_OS=1 428 else 429 OLD_OS_ATTR=" os='$OS'" 430 fi 431 432 cat >> "$OUT" <<EOFA 433 <sdk:archive$OLD_OS_ATTR> 434 <sdk:size>$SIZE</sdk:size> 435 <sdk:checksum type='sha1'>$SHA1</sdk:checksum> 436 <sdk:url>$DST</sdk:url> 437EOFA 438 if [[ $USE_HOST_OS ]]; then 439 # parse the Archive.Host/Jvm info from the source.props if present 440 MAP=$(parse_attributes "$PROPS" ${ATTRS_ARCHIVE[@]}) 441 # Always generate host-os if not present 442 if [[ "${MAP/ host-os /}" == "$MAP" ]]; then 443 MAP="$MAP host-os $OS" 444 fi 445 output_attributes "archive" "$OUT" $MAP 446 fi 447 echo " </sdk:archive>" >> "$OUT" 448 449 # Skip to next arch/zip entry. 450 # If not a valid OS, close the archives/package nodes. 451 OS=$(check_enum "$1" $OSES) 452 453 if [[ ! "$OS" ]]; then 454 echo " </sdk:archives>" >> "$OUT" 455 echo " </sdk:${ELEMENT}>" >> "$OUT" 456 fi 457 done 458 459done 460 461# Generate XML footer 462echo "</sdk:$ROOT>" >> "$OUT" 463 464#echo "## Validate XML against schema" 465$XMLLINT --noout --schema $SCHEMA "$OUT" 466