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 # Note: addon extra revision can't take a preview number. We don't enforce
    144 # this in this script. Instead schema validation will fail if the extra
    145 # source.property declares an RC and it gets inserted in the addon.xml here.
    146 
    147 if [[ "$ROOT" == "sdk-repository" && "$XSD_VERSION" -ge 7 ]] ||
    148    [[ "$ROOT" == "sdk-addon"      && "$XSD_VERSION" -ge 5 ]]; then
    149 FULL_REVISIONS=(
    150   tool          revision
    151   build-tool    revision
    152   platform-tool revision
    153   extra         revision
    154   @             min-tools-rev
    155   @             min-platform-tools-rev
    156 )
    157 else
    158 FULL_REVISIONS=()
    159 fi
    160 
    161 
    162 # Parse all archives.
    163 
    164 function needs_full_revision() {
    165   local PARENT="$1"
    166   local ELEMENT="$2"
    167   shift
    168   shift
    169   local P E
    170 
    171   while [[ "$1" ]]; do
    172     P=$1
    173     E=$2
    174     if [[ "$E" == "$ELEMENT" ]] && [[ "$P" == "@" || "$P" == "$PARENT" ]]; then
    175       return 0 # true
    176     fi
    177     shift
    178     shift
    179   done
    180 
    181   return 1 # false
    182 }
    183 
    184 # Parses and print a full revision in the form "1.2.3 rc4".
    185 # Note that the format requires to have 1 space before the
    186 # optional "rc" (e.g. '1 rc4', not '1rc4') and no space after
    187 # the rc (so not '1 rc 4' either)
    188 function write_full_revision() {
    189   local VALUE="$1"
    190   local EXTRA_SPACE="$2"
    191   local KEYS="major minor micro preview"
    192   local V K
    193 
    194   while [[ -n "$VALUE" && -n "$KEYS" ]]; do
    195     # Take 1st segment delimited by . or space
    196     V="${VALUE%%[. ]*}"
    197 
    198     # Print it
    199     if [[ "${V:0:2}" == "rc" ]]; then
    200       V="${V:2}"
    201       K="preview"
    202       KEYS=""
    203     else
    204       K="${KEYS%% *}"
    205     fi
    206 
    207     if [[ -n "$V" && -n "$K" ]]; then
    208         echo "$EXTRA_SPACE            <sdk:$K>$V</sdk:$K>"
    209     fi
    210 
    211     # Take the rest.
    212     K="${KEYS#* }"
    213     if [[ "$K" == "$KEYS" ]]; then KEYS=""; else KEYS="$K"; fi
    214     V="${VALUE#*[. ]}"
    215     if [[ "$V" == "$VALUE" ]]; then VALUE=""; else VALUE="$V"; fi
    216   done
    217 }
    218 
    219 
    220 function parse_attributes() {
    221   local PROPS="$1"
    222   shift
    223   local RESULT=""
    224   local VALUE
    225   local REV
    226   local USED
    227 
    228   # $1 here is the ATTRS list above.
    229   while [[ "$1" ]]; do
    230     # Check the version in which the attribute was introduced and
    231     # ignore things which are too *new* for this schema. This lets
    232     # us generate old schemas for backward compatibility purposes.
    233     SRC=$1
    234     DST=$2
    235     REV=$3
    236 
    237     if [[ $XSD_VERSION -ge $REV ]]; then
    238       # Parse the property, if present. Any space is replaced by @
    239       VALUE=$( grep "^$SRC=" "$PROPS" | cut -d = -f 2 | tr ' ' '@' | tr -d '\r' )
    240       if [[ -n "$VALUE" ]]; then
    241         # In case an XML element would be mapped multiple times,
    242         # only use its first definition.
    243         if [[ "${USED/$DST/}" == "$USED" ]]; then
    244           USED="$USED $DST"
    245           RESULT="$RESULT $DST $VALUE"
    246         fi
    247       fi
    248     fi
    249     shift
    250     shift
    251     shift
    252   done
    253 
    254   echo "$RESULT"
    255 }
    256 
    257 function output_attributes() {
    258   local ELEMENT="$1"
    259   local OUT="$2"
    260   shift
    261   shift
    262   local KEY VALUE
    263   local NODE LAST_NODE EXTRA_SPACE
    264 
    265   while [[ "$1" ]]; do
    266     KEY="$1"
    267     VALUE="${2//@/ }"
    268     NODE="${KEY%%/*}"
    269     KEY="${KEY##*/}"
    270     if [[ "$NODE" == "$KEY" ]]; then
    271       NODE=""
    272       EXTRA_SPACE=""
    273     fi
    274     if [[ "$NODE" != "$LAST_NODE" ]]; then
    275       EXTRA_SPACE="    "
    276       [[ "$LAST_NODE" ]] && echo "          </sdk:$LAST_NODE>" >> "$OUT"
    277       LAST_NODE="$NODE"
    278       [[ "$NODE"      ]] && echo "          <sdk:$NODE>" >> "$OUT"
    279     fi
    280     if needs_full_revision "$ELEMENT" "$KEY" ${FULL_REVISIONS[@]}; then
    281       echo "$EXTRA_SPACE        <sdk:$KEY>"       >> "$OUT"
    282       write_full_revision "$VALUE" "$EXTRA_SPACE" >> "$OUT"
    283       echo "$EXTRA_SPACE        </sdk:$KEY>"      >> "$OUT"
    284     else
    285       echo "$EXTRA_SPACE        <sdk:$KEY>$VALUE</sdk:$KEY>" >> "$OUT"
    286     fi
    287     shift
    288     shift
    289   done
    290   if [[ "$LAST_NODE" ]]; then echo "          </sdk:$LAST_NODE>" >> "$OUT"; fi
    291 }
    292 
    293 while [[ -n "$1" ]]; do
    294   # Process archives.
    295   # First we expect a type. For convenience the type can be plural.
    296   TYPE=$(check_enum "${1%%s}" $TYPES)
    297   [[ -z $TYPE ]] && error "Unknown archive type '$1'."
    298   shift
    299 
    300   ELEMENT="$TYPE"
    301   # The element name is different for extras:
    302   [[ "$TYPE" == "support" ]] && ELEMENT="extra"
    303 
    304   MAP=""
    305   FIRST="1"
    306   LIBS_XML=""
    307 
    308   OS=$(check_enum "$1" $OSES)
    309   while [[ $OS ]]; do
    310     shift
    311     [[ $OS == "linux-x86" ]] && OS=linux
    312     [[ $OS == "darwin" ]] && OS=macosx
    313 
    314     SRC="$1"
    315     DST="$1"
    316     if [[ "${SRC/:/}" != "$SRC" ]]; then
    317       DST="${SRC/*:/}"
    318       SRC="${SRC/:*/}"
    319     fi
    320     [[ ! -f "$SRC" ]] && error "Missing file for archive $TYPE/$OS: $SRC"
    321     shift
    322 
    323     # Depending on the archive type, we need a number of attributes
    324     # from the source.properties or the manifest.ini. We'll take
    325     # these attributes from the first zip found.
    326     #
    327     # What we need vs. which package uses it:
    328     # - description             all
    329     # - revision                all
    330     # - version                 platform
    331     # - included-abi            platform
    332     # - api-level               platform sample doc add-on system-image
    333     # - codename                platform sample doc add-on system-image
    334     # - min-tools-rev           platform sample
    335     # - min-platform-tools-rev  tool
    336     # - min-api-level           extra
    337     # - vendor                  extra               add-on
    338     # - path                    extra
    339     # - old-paths               extra
    340     # - abi                     system-image
    341     #
    342     # We don't actually validate here.
    343     # Just take whatever is defined and put it in the XML.
    344     # XML validation against the schema will be done at the end.
    345 
    346     if [[ $FIRST ]]; then
    347       FIRST=""
    348 
    349       if unzip -t "$SRC" | grep -qs "source.properties" ; then
    350         # Extract Source Properties
    351         # unzip: -j=flat (no dirs), -q=quiet, -o=overwrite, -d=dest dir
    352         unzip -j -q -o -d "$TMP_DIR" "$SRC" "*/source.properties"
    353         PROPS="$TMP_DIR/source.properties"
    354 
    355       elif unzip -t "$SRC" | grep -qs "manifest.ini" ; then
    356         unzip -j -q -o -d "$TMP_DIR" "$SRC" "*/manifest.ini"
    357         PROPS="$TMP_DIR/manifest.ini"
    358 
    359         # Parse the libs for an addon and generate the <libs> node
    360         # libraries is a semi-colon separated list
    361         LIBS=$(parse_attributes "$PROPS" "libraries")
    362         LIBS_XML="        <sdk:libs>"
    363         for LIB in ${LIBS//;/ }; do
    364           LIBS_XML="$LIBS_XML
    365            <sdk:lib><sdk:name>$LIB</sdk:name></sdk:lib>"
    366         done
    367         LIBS_XML="$LIBS_XML
    368         </sdk:libs>"
    369 
    370       else
    371         error "Failed to find source.properties or manifest.ini in $SRC"
    372       fi
    373 
    374       [[ ! -f $PROPS ]] && error "Failed to extract $PROPS from $SRC"
    375       MAP=$(parse_attributes "$PROPS" ${ATTRS[@]})
    376 
    377       # Time to generate the XML for the package
    378       echo "    <sdk:${ELEMENT}>" >> "$OUT"
    379       output_attributes "$ELEMENT" "$OUT" $MAP
    380       [[ -n "$LIBS_XML" ]] && echo "$LIBS_XML" >> "$OUT"
    381       echo "        <sdk:archives>" >> "$OUT"
    382     fi
    383 
    384     # Generate archive info
    385     echo "## Add $TYPE/$OS archive $SRC"
    386     if [[ $( uname ) == "Darwin" ]]; then
    387       SIZE=$( stat -f %z "$SRC" )
    388     else
    389       SIZE=$( stat -c %s "$SRC" )
    390     fi
    391     SHA1=$( sha1sum "$SRC" | cut -d " "  -f 1 )
    392 
    393     cat >> "$OUT" <<EOFA
    394             <sdk:archive os='$OS' arch='any'>
    395                 <sdk:size>$SIZE</sdk:size>
    396                 <sdk:checksum type='sha1'>$SHA1</sdk:checksum>
    397                 <sdk:url>$DST</sdk:url>
    398             </sdk:archive>
    399 EOFA
    400 
    401     # Skip to next arch/zip entry.
    402     # If not a valid OS, close the archives/package nodes.
    403     OS=$(check_enum "$1" $OSES)
    404 
    405     if [[ ! "$OS" ]]; then
    406       echo "        </sdk:archives>" >> "$OUT"
    407       echo "    </sdk:${ELEMENT}>" >> "$OUT"
    408     fi
    409   done
    410 
    411 done
    412 
    413 # Generate XML footer
    414 echo "</sdk:$ROOT>" >> "$OUT"
    415 
    416 echo "## Validate XML against schema"
    417 xmllint --schema $SCHEMA "$OUT"
    418 
    419