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