SDK Manager: phase 1 of support for future schemas

This is phase 1 of the support for future schemas:
- introduces a new v2 of the XSD
- handles an unknown XSD and extract its tools elements after
  some hand checks.

TODO in phase 2:
- we still need to iron out the details of the sample node.
- there's no support for samples yet... this commit is not
  about switching the sample code support to the new structure.
- there's no indication to the user that we are handling a new
  schema... there should be some visual clue on why only the
  tools are shown.

SDK BUG 2252825

Change-Id: If1bda26414cfe15275063d3187a98be6301d2992
This commit is contained in:
Raphael
2009-11-14 21:30:27 -08:00
parent d2ad5e31b8
commit 580e982719
12 changed files with 1447 additions and 180 deletions

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdklib.internal.repository;
import com.android.sdklib.repository.SdkRepository;
import org.w3c.dom.Document;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import junit.framework.TestCase;
/**
* Tests for {@link RepoSource}
*/
public class RepoSourceTest extends TestCase {
private static class MockMonitor implements ITaskMonitor {
public void setResult(String resultFormat, Object... args) {
}
public void setProgressMax(int max) {
}
public void setDescription(String descriptionFormat, Object... args) {
}
public boolean isCancelRequested() {
return false;
}
public void incProgress(int delta) {
}
public int getProgress() {
return 0;
}
public boolean displayPrompt(String title, String message) {
return false;
}
public ITaskMonitor createSubMonitor(int tickCount) {
return null;
}
}
/**
* An internal helper class to give us visibility to the protected members we want
* to test.
*/
private static class MockRepoSource extends RepoSource {
public MockRepoSource() {
super("fake-url", false /*userSource*/);
}
public Document _findAlternateToolsXml(InputStream xml) {
return super.findAlternateToolsXml(xml);
}
public boolean _parsePackages(Document doc, String nsUri, ITaskMonitor monitor) {
return super.parsePackages(doc, nsUri, monitor);
}
}
private MockRepoSource mSource;
@Override
protected void setUp() throws Exception {
super.setUp();
mSource = new MockRepoSource();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
mSource = null;
}
public void testFindAlternateToolsXml_Errors() {
// Support null as input
Document result = mSource._findAlternateToolsXml(null);
assertNull(result);
// Support an empty input
String str = "";
ByteArrayInputStream input = new ByteArrayInputStream(str.getBytes());
result = mSource._findAlternateToolsXml(input);
assertNull(result);
// Support a random string as input
str = "Some random string, not even HTML nor XML";
input = new ByteArrayInputStream(str.getBytes());
result = mSource._findAlternateToolsXml(input);
assertNull(result);
// Support an HTML input, e.g. a typical 404 document as returned by DL
str = "<html><head> " +
"<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"> " +
"<title>404 Not Found</title> " + "<style><!--" + "body {font-family: arial,sans-serif}" +
"div.nav { ... blah blah more css here ... color: green}" +
"//--></style> " + "<script><!--" + "var rc=404;" + "//-->" + "</script> " + "</head> " +
"<body text=#000000 bgcolor=#ffffff> " +
"<table border=0 cellpadding=2 cellspacing=0 width=100%><tr><td rowspan=3 width=1% nowrap> " +
"<b><font face=times color=#0039b6 size=10>G</font><font face=times color=#c41200 size=10>o</font><font face=times color=#f3c518 size=10>o</font><font face=times color=#0039b6 size=10>g</font><font face=times color=#30a72f size=10>l</font><font face=times color=#c41200 size=10>e</font>&nbsp;&nbsp;</b> " +
"<td>&nbsp;</td></tr> " +
"<tr><td bgcolor=\"#3366cc\"><font face=arial,sans-serif color=\"#ffffff\"><b>Error</b></td></tr> " +
"<tr><td>&nbsp;</td></tr></table> " + "<blockquote> " + "<H1>Not Found</H1> " +
"The requested URL <code>/404</code> was not found on this server." + " " + "<p> " +
"</blockquote> " +
"<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=\"#3366cc\"><img alt=\"\" width=1 height=4></td></tr></table> " +
"</body></html> ";
input = new ByteArrayInputStream(str.getBytes());
result = mSource._findAlternateToolsXml(input);
assertNull(result);
// Support some random XML document, totally unrelated to our sdk-repository schema
str = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"" +
" package=\"some.cool.app\" android:versionName=\"1.6.04\" android:versionCode=\"1604\">" +
" <application android:label=\"@string/app_name\" android:icon=\"@drawable/icon\"/>" +
"</manifest>";
input = new ByteArrayInputStream(str.getBytes());
result = mSource._findAlternateToolsXml(input);
assertNull(result);
}
public void testFindAlternateToolsXml_1() {
InputStream xmlStream = this.getClass().getResourceAsStream(
"/com/android/sdklib/testdata/repository_sample_1.xml");
Document result = mSource._findAlternateToolsXml(xmlStream);
assertNotNull(result);
assertTrue(mSource._parsePackages(result,
SdkRepository.NS_SDK_REPOSITORY, new MockMonitor()));
// check the packages we found... we expected to find 2 tool packages with 1 archive each.
Package[] pkgs = mSource.getPackages();
assertEquals(2, pkgs.length);
for (Package p : pkgs) {
assertEquals(ToolPackage.class, p.getClass());
assertEquals(1, p.getArchives().length);
}
}
}

View File

@@ -38,7 +38,7 @@ import junit.framework.TestCase;
* References:
* http://www.ibm.com/developerworks/xml/library/x-javaxmlvalidapi.html
*/
public class TestSdkRepository extends TestCase {
public class SdkRepositoryTest extends TestCase {
@Override
protected void setUp() throws Exception {
@@ -59,10 +59,12 @@ public class TestSdkRepository extends TestCase {
private String mWarnings = "";
private String mErrors = "";
@SuppressWarnings("unused")
public String getErrors() {
return mErrors;
}
@SuppressWarnings("unused")
public String getWarnings() {
return mWarnings;
}
@@ -109,8 +111,8 @@ public class TestSdkRepository extends TestCase {
// --- Helpers ------------
/** Helper method that returns a validator for our XSD */
private Validator getValidator(CaptureErrorHandler handler) throws SAXException {
InputStream xsdStream = SdkRepository.getXsdStream();
private Validator getValidator(int version, CaptureErrorHandler handler) throws SAXException {
InputStream xsdStream = SdkRepository.getXsdStream(version);
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new StreamSource(xsdStream));
Validator validator = schema.newValidator();
@@ -121,19 +123,6 @@ public class TestSdkRepository extends TestCase {
return validator;
}
/** Validate a valid sample using an InputStream */
public void testValidateLocalRepositoryFile() throws Exception {
InputStream xmlStream =
TestSdkRepository.class.getResourceAsStream(
"/com/android/sdklib/testdata/repository_sample.xml");
Source source = new StreamSource(xmlStream);
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = getValidator(handler);
validator.validate(source);
handler.verify();
}
/** An helper that validates a string against an expected regexp. */
private void assertRegex(String expectedRegexp, String actualString) {
assertNotNull(actualString);
@@ -145,6 +134,49 @@ public class TestSdkRepository extends TestCase {
// --- Tests ------------
/** Validate a valid sample using namespace version 1 using an InputStream */
public void testValidateLocalRepositoryFile1() throws Exception {
InputStream xmlStream = this.getClass().getResourceAsStream(
"/com/android/sdklib/testdata/repository_sample_1.xml");
Source source = new StreamSource(xmlStream);
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = getValidator(1, handler);
validator.validate(source);
handler.verify();
}
/** Validate a valid sample using namespace version 2 using an InputStream */
public void testValidateLocalRepositoryFile2() throws Exception {
InputStream xmlStream = this.getClass().getResourceAsStream(
"/com/android/sdklib/testdata/repository_sample_2.xml");
Source source = new StreamSource(xmlStream);
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = getValidator(2, handler);
validator.validate(source);
handler.verify();
}
/** Test that validating a v2 file using the v1 schema fails. */
public void testValidateFile2UsingNs1() throws Exception {
InputStream xmlStream = this.getClass().getResourceAsStream(
"/com/android/sdklib/testdata/repository_sample_2.xml");
Source source = new StreamSource(xmlStream);
Validator validator = getValidator(1, null); // validate v2 against v1... fail!
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect to get this specific exception message
assertRegex("cvc-elt.1: Cannot find the declaration of element 'sdk:sdk-repository'.*", e.getMessage());
return;
}
// We shouldn't get here
fail();
}
/** A document should at least have a root to be valid */
public void testEmptyXml() throws Exception {
String document = "<?xml version=\"1.0\"?>";
@@ -152,7 +184,7 @@ public class TestSdkRepository extends TestCase {
Source source = new StreamSource(new StringReader(document));
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = getValidator(handler);
Validator validator = getValidator(SdkRepository.XSD_LATEST_VERSION, handler);
try {
validator.validate(source);
@@ -166,15 +198,23 @@ public class TestSdkRepository extends TestCase {
fail();
}
private static String OPEN_TAG =
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/" +
Integer.toString(SdkRepository.XSD_LATEST_VERSION) +
"\">";
private static String CLOSE_TAG = "</r:sdk-repository>";
/** A document with a root element containing no platform, addon, etc., is valid. */
public void testEmptyRootXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" />";
OPEN_TAG +
CLOSE_TAG;
Source source = new StreamSource(new StringReader(document));
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = getValidator(handler);
Validator validator = getValidator(SdkRepository.XSD_LATEST_VERSION, handler);
validator.validate(source);
handler.verify();
}
@@ -182,14 +222,14 @@ public class TestSdkRepository extends TestCase {
/** A document with an unknown element. */
public void testUnknownContentXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +
OPEN_TAG +
"<r:unknown />" +
"</r:sdk-repository>";
CLOSE_TAG;
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = getValidator(null);
Validator validator = getValidator(SdkRepository.XSD_LATEST_VERSION, null);
try {
validator.validate(source);
} catch (SAXParseException e) {
@@ -204,14 +244,14 @@ public class TestSdkRepository extends TestCase {
/** A document with an incomplete element. */
public void testIncompleteContentXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +
OPEN_TAG +
"<r:platform> <r:api-level>1</r:api-level> <r:libs /> </r:platform>" +
"</r:sdk-repository>";
CLOSE_TAG;
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = getValidator(null);
Validator validator = getValidator(SdkRepository.XSD_LATEST_VERSION, null);
try {
validator.validate(source);
} catch (SAXParseException e) {
@@ -226,14 +266,14 @@ public class TestSdkRepository extends TestCase {
/** A document with a wrong type element. */
public void testWrongTypeContentXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +
OPEN_TAG +
"<r:platform> <r:api-level>NotAnInteger</r:api-level> <r:libs /> </r:platform>" +
"</r:sdk-repository>";
CLOSE_TAG;
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = getValidator(null);
Validator validator = getValidator(SdkRepository.XSD_LATEST_VERSION, null);
try {
validator.validate(source);
} catch (SAXParseException e) {
@@ -250,17 +290,17 @@ public class TestSdkRepository extends TestCase {
public void testLicenseIdNotFound() throws Exception {
// we define a license named "lic1" and then reference "lic2" instead
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +
OPEN_TAG +
"<r:license id=\"lic1\"> some license </r:license> " +
"<r:tool> <r:uses-license ref=\"lic2\" /> <r:revision>1</r:revision> " +
"<r:archives> <r:archive os=\"any\"> <r:size>1</r:size> <r:checksum>2822ae37115ebf13412bbef91339ee0d9454525e</r:checksum> " +
"<r:url>url</r:url> </r:archive> </r:archives> </r:tool>" +
"</r:sdk-repository>";
CLOSE_TAG;
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = getValidator(null);
Validator validator = getValidator(SdkRepository.XSD_LATEST_VERSION, null);
try {
validator.validate(source);
} catch (SAXParseException e) {
@@ -277,16 +317,16 @@ public class TestSdkRepository extends TestCase {
public void testExtraPathWithSlash() throws Exception {
// we define a license named "lic1" and then reference "lic2" instead
String document = "<?xml version=\"1.0\"?>" +
"<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +
OPEN_TAG +
"<r:extra> <r:revision>1</r:revision> <r:path>path/cannot\\contain\\segments</r:path> " +
"<r:archives> <r:archive os=\"any\"> <r:size>1</r:size> <r:checksum>2822ae37115ebf13412bbef91339ee0d9454525e</r:checksum> " +
"<r:url>url</r:url> </r:archive> </r:archives> </r:extra>" +
"</r:sdk-repository>";
CLOSE_TAG;
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = getValidator(null);
Validator validator = getValidator(SdkRepository.XSD_LATEST_VERSION, null);
try {
validator.validate(source);
} catch (SAXParseException e) {

View File

@@ -0,0 +1,288 @@
<?xml version="1.0"?>
<!--
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<sdk:sdk-repository
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sdk="http://schemas.android.com/sdk/android/repository/2">
<!-- Define a couple of licenses. These will be referenced by uses-license later. -->
<sdk:license type="text" id="license1">
This is the license
for this platform.
</sdk:license>
<sdk:license id="license2">
Licenses are only of type 'text' right now, so this is implied.
</sdk:license>
<!-- Inner elements must be either platform, add-on, doc or tool.
There can be 0 or more of each, in any order. -->
<sdk:platform>
<sdk:version>1.0</sdk:version>
<sdk:api-level>1</sdk:api-level>
<sdk:revision>3</sdk:revision>
<sdk:uses-license ref="license1" />
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>
<sdk:release-note>This is an optional release note
for this package. It's a free multi-line text.
</sdk:release-note>
<sdk:release-url>http://some/url/for/the/release/note.html</sdk:release-url>
<sdk:min-tools-rev>2</sdk:min-tools-rev>
<!-- The archives node is mandatory and it cannot be empty. -->
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/files/plat1.zip</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:platform>
<sdk:doc>
<sdk:api-level>1</sdk:api-level>
<sdk:revision>1</sdk:revision>
<!-- the license element is not mandatory. -->
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/docs.html</sdk:desc-url>
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/docs/docs1.zip</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:doc>
<sdk:add-on>
<sdk:name>My First add-on</sdk:name>
<sdk:api-level>1</sdk:api-level>
<sdk:vendor>John Doe</sdk:vendor>
<sdk:revision>1</sdk:revision>
<sdk:uses-license ref="license2" />
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/myfirstaddon</sdk:desc-url>
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/add-ons/first.zip</sdk:url>
</sdk:archive>
</sdk:archives>
<!-- The libs node is mandatory, however it can be empty. -->
<sdk:libs>
<sdk:lib>
<sdk:name>android.blah.somelib</sdk:name>
<sdk:description>The description for this library.</sdk:description>
</sdk:lib>
<sdk:lib>
<!-- sdk:description is optional, name is not -->
<sdk:name>com.android.mymaps</sdk:name>
</sdk:lib>
</sdk:libs>
</sdk:add-on>
<sdk:platform>
<sdk:version>1.1</sdk:version>
<sdk:api-level>2</sdk:api-level>
<sdk:revision>12</sdk:revision>
<sdk:uses-license ref="license1" />
<!-- sdk:description and sdk:desc-url are optional -->
<sdk:archives>
<sdk:archive os="windows">
<!-- arch attribute is optional -->
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/platform-2-12-win.zip</sdk:url>
</sdk:archive>
<sdk:archive os="macosx" arch="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/platform-2-12-mac.zip</sdk:url>
</sdk:archive>
<sdk:archive os="macosx" arch="ppc">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/platform-2-12-mac.zip</sdk:url>
</sdk:archive>
<sdk:archive os="linux" arch="x86">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/platform-2-12-linux.tar.bz2</sdk:url>
</sdk:archive>
<sdk:archive os="linux" arch="x86_64">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/platform-2-12-linux.tar.bz2</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:platform>
<sdk:add-on>
<sdk:name>My Second add-on</sdk:name>
<sdk:api-level>2</sdk:api-level>
<sdk:vendor>John Deer</sdk:vendor>
<sdk:revision>42</sdk:revision>
<sdk:archives>
<sdk:archive os="windows">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/second-42-win.zip</sdk:url>
</sdk:archive>
<sdk:archive os="linux">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/second-42-linux.tar.bz2</sdk:url>
</sdk:archive>
</sdk:archives>
<sdk:libs>
<sdk:lib>
<sdk:name>android.blah.somelib</sdk:name>
<sdk:description>The description for this library.</sdk:description>
</sdk:lib>
<sdk:lib>
<sdk:name>com.android.mymaps</sdk:name>
</sdk:lib>
</sdk:libs>
<sdk:uses-license ref="license2" />
</sdk:add-on>
<sdk:platform>
<sdk:version>Pastry</sdk:version>
<sdk:api-level>5</sdk:api-level>
<sdk:codename>Pastry</sdk:codename>
<sdk:revision>3</sdk:revision>
<sdk:uses-license ref="license1" />
<sdk:description>Preview version for Pastry</sdk:description>
<sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>
<!-- The archives node is mandatory and it cannot be empty. -->
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/files/plat1.zip</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:platform>
<sdk:tool>
<sdk:revision>1</sdk:revision>
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/tools.html</sdk:desc-url>
<sdk:uses-license ref="license1" />
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>http://www.example.com/files/tools1.zip</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:tool>
<sdk:doc>
<sdk:api-level>2</sdk:api-level>
<sdk:revision>42</sdk:revision>
<sdk:uses-license ref="license2" />
<sdk:archives>
<sdk:archive os="windows">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/docs/2.zip</sdk:url>
</sdk:archive>
<sdk:archive os="linux">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/docs2-linux.tar.bz2</sdk:url>
</sdk:archive>
<sdk:archive os="macosx">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/docs2-mac.tar.bz2</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:doc>
<sdk:tool>
<sdk:revision>42</sdk:revision>
<sdk:uses-license ref="license1" />
<sdk:archives>
<sdk:archive os="windows">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/tools/2.zip</sdk:url>
</sdk:archive>
<sdk:archive os="linux">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/tools2-linux.tar.bz2</sdk:url>
</sdk:archive>
<sdk:archive os="macosx">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/tools2-mac.tar.bz2</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:tool>
<sdk:add-on>
<sdk:uses-license ref="license2" />
<sdk:name>This add-on has no libraries</sdk:name>
<sdk:api-level>4</sdk:api-level>
<sdk:vendor>Joe Bar</sdk:vendor>
<sdk:revision>3</sdk:revision>
<sdk:archives>
<sdk:archive os="any" arch="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/imnotanarchiveimadoctorjim.zip</sdk:url>
</sdk:archive>
</sdk:archives>
<!-- The libs node is mandatory, however it can be empty. -->
<sdk:libs />
</sdk:add-on>
<sdk:extra>
<sdk:path>usb_driver</sdk:path>
<sdk:uses-license ref="license2" />
<sdk:revision>43</sdk:revision>
<sdk:archives>
<sdk:archive os="any" arch="any">
<sdk:size>65536</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
<sdk:url>distrib/extraduff.zip</sdk:url>
</sdk:archive>
</sdk:archives>
<sdk:description>An Extra package for the USB driver, it will install in $SDK/usb_driver</sdk:description>
<sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>
<sdk:min-tools-rev>3</sdk:min-tools-rev>
</sdk:extra>
<sdk:sample>
<sdk:uses-license ref="license2" />
<sdk:api-level>1234</sdk:api-level>
<sdk:revision>314153</sdk:revision>
<sdk:archives>
<sdk:archive os="any" arch="any">
<sdk:size>123456</sdk:size>
<sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d94545228</sdk:checksum>
<sdk:url>archives/samples/dream-1234.zip</sdk:url>
</sdk:archive>
</sdk:archives>
</sdk:sample>
</sdk:sdk-repository>