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