=== modified file 'linaro-hwpack-install'
--- linaro-hwpack-install	2012-07-05 14:44:04 +0000
+++ linaro-hwpack-install	2012-07-23 15:28:30 +0000
@@ -41,7 +41,7 @@
 FORCE_YES="no"
 SOURCES_LIST_FILE="${TEMP_DIR}/sources.list"
 APT_GET_OPTIONS="Dir::Etc::SourceList=${SOURCES_LIST_FILE}"
-SUPPORTED_FORMATS="1.0 2.0"  # A space-separated list of hwpack formats.
+SUPPORTED_FORMATS="1.0 2.0 3.0"  # A space-separated list of hwpack formats.
 
 sudo="sudo"
 if [ $(id -u) -eq 0 ]; then
@@ -53,12 +53,15 @@
   exit 1
 }
 
-usage_msg="Usage: $(basename $0) [--install-latest] [--force-yes] HWPACK_TARBALL"
+usage_msg="Usage: $(basename $0) [--install-latest] [--force-yes] --hwpack-version <version> --hwpack-arch <architecture> --hwpack-name <name> HWPACK_TARBALL"
 if [ $# -eq 0 ]; then
   die $usage_msg
 fi
 
 HWPACK_TARBALL_FOUND="no"
+HWPACK_VERSION=""
+HWPACK_ARCH=""
+HWPACK_NAME=""
 
 while [ $# -gt 0 ]; do
   case "$1" in 
@@ -68,6 +71,18 @@
     --force-yes)
       FORCE_YES="yes"
       shift;;
+    --hwpack-version)
+      HWPACK_VERSION=$2
+      shift;
+      shift;;
+    --hwpack-arch)
+      HWPACK_ARCH=$2
+      shift;
+      shift;;
+    --hwpack-name)
+      HWPACK_NAME=$2
+      shift;
+      shift;;
     --*)
       die $usage_msg "\nUnrecognized option: \"$1\"";;
     *)
@@ -79,6 +94,9 @@
 done
 
 [ "$HWPACK_TARBALL_FOUND" = "no" ] && die $usage_msg
+[ "$HWPACK_VERSION" = "" ] && die $usage_msg
+[ "$HWPACK_ARCH" = "" ] && die $usage_msg
+[ "$HWPACK_NAME" = "" ] && die $usage_msg
 
 # Try to acquire fd #9 (i.e. /var/lock/hwpack) for 2 seconds.
 # Using 9 as the file descriptor because of https://launchpad.net/bugs/249620
@@ -125,7 +143,6 @@
       "Try using a newer version of $(basename $0)."
 
 # Check the architecture of the hwpack matches that of the host system.
-HWPACK_ARCH=`grep ARCHITECTURE "${HWPACK_DIR}/metadata" | cut -d "=" -f2`
 [ "$HWPACK_ARCH" == `dpkg --print-architecture` ] || \
   die "Hardware pack architecture ($HWPACK_ARCH) does not match the host's architecture"
 
@@ -209,8 +226,6 @@
 # For "older" hwpacks that don't have a dependency package, we just
 # manually install the contents of the hwpack.
 
-HWPACK_NAME=`grep NAME "${HWPACK_DIR}/metadata" | cut -d "=" -f2`
-HWPACK_VERSION=`grep VERSION "${HWPACK_DIR}/metadata" | cut -d "=" -f2`
 dependency_package="hwpack-${HWPACK_NAME}"
 if grep -q "^${dependency_package}=${HWPACK_VERSION}\$" "${HWPACK_DIR}"/manifest; then
   DEP_PACKAGE_PRESENT="yes"

=== modified file 'linaro_image_tools/hwpack/builder.py'
--- linaro_image_tools/hwpack/builder.py	2012-06-13 14:53:32 +0000
+++ linaro_image_tools/hwpack/builder.py	2012-07-23 15:36:00 +0000
@@ -36,6 +36,13 @@
     PackageFetcher,
     )
 
+from linaro_image_tools.hwpack.hwpack_fields import (
+    PACKAGE_FIELD,
+    SPL_PACKAGE_FIELD,
+)
+
+PACKAGE_FIELDS = [PACKAGE_FIELD, SPL_PACKAGE_FIELD]
+
 
 logger = logging.getLogger(__name__)
 
@@ -110,6 +117,22 @@
             package.filepath, wanted_file)
         return hwpack.add_file(target_path, tempfile_name)
 
+    def find_bootloader_packages(self, bootloaders_config):
+        """Loop through the bootloaders dictionary searching for packages
+        that should be installed, based on known keywords.
+
+        :param bootloaders_config: The bootloaders dictionary to loop through.
+        :return A list of packages, without duplicates."""
+        boot_packages = []
+        for key, value in bootloaders_config.iteritems():
+            if isinstance(value, dict):
+                boot_packages.extend(self.find_bootloader_packages(value))
+            else:
+                if key in PACKAGE_FIELDS:
+                    boot_packages.append(value)
+        # Eliminate duplicates.
+        return list(set(boot_packages))
+
     def build(self):
         for architecture in self.config.architectures:
             logger.info("Building for %s" % architecture)
@@ -121,10 +144,18 @@
                 hwpack.add_apt_sources(sources)
                 sources = sources.values()
                 packages = self.config.packages[:]
-                if self.config.u_boot_package is not None:
-                    packages.append(self.config.u_boot_package)
-                if self.config.spl_package is not None:
-                    packages.append(self.config.spl_package)
+                # Loop through multiple bootloaders.
+                # In V3 of hwpack configuration, all the bootloaders info and
+                # packages are in the bootloaders section.
+                if self.config.format.format_as_string == '3.0':
+                    if self.config.bootloaders:
+                        packages.extend(self.find_bootloader_packages(
+                                            self.config.bootloaders))
+                else:
+                    if self.config.u_boot_package is not None:
+                        packages.append(self.config.u_boot_package)
+                    if self.config.spl_package is not None:
+                        packages.append(self.config.spl_package)
                 local_packages = [
                     FetchedPackage.from_deb(deb)
                     for deb in self.local_debs]

=== modified file 'linaro_image_tools/hwpack/config.py'
--- linaro_image_tools/hwpack/config.py	2012-06-13 14:53:32 +0000
+++ linaro_image_tools/hwpack/config.py	2012-07-23 14:14:31 +0000
@@ -20,14 +20,68 @@
 # USA.
 
 import ConfigParser
+from operator import attrgetter
 import re
 import string
+import yaml
 
 from linaro_image_tools.hwpack.hardwarepack_format import (
     HardwarePackFormatV1,
     HardwarePackFormatV2,
+    HardwarePackFormatV3,
     )
 
+from hwpack_fields import (
+    ARCHITECTURES_FIELD,
+    ARCHITECTURE_FIELD,
+    ASSUME_INSTALLED_FIELD,
+    BOARDS_FIELD,
+    BOOTLOADERS_FIELD,
+    BOOT_MIN_SIZE_FIELD,
+    BOOT_SCRIPT_FIELD,
+    DD_FIELD,
+    DTB_ADDR_FIELD,
+    DTB_FILE_FIELD,
+    ENV_DD_FIELD,
+    EXTRA_BOOT_OPTIONS_FIELD,
+    EXTRA_SERIAL_OPTIONS_FIELD,
+    FILE_FIELD,
+    FORMAT_FIELD,
+    INCLUDE_DEBS_FIELD,
+    IN_BOOT_PART_FIELD,
+    INITRD_ADDR_FIELD,
+    INITRD_FILE_FIELD,
+    KERNEL_ADDR_FIELD,
+    KERNEL_FILE_FIELD,
+    LOAD_ADDR_FIELD,
+    LOADER_MIN_SIZE_FIELD,
+    LOADER_START_FIELD,
+    MAINTAINER_FIELD,
+    MMC_ID_FIELD,
+    NAME_FIELD,
+    ORIGIN_FIELD,
+    PACKAGE_FIELD,
+    PACKAGES_FIELD,
+    PARTITION_LAYOUT_FIELD,
+    ROOT_MIN_SIZE_FIELD,
+    SAMSUNG_BL1_LEN_FIELD,
+    SAMSUNG_BL1_START_FIELD,
+    SAMSUNG_BL2_LEN_FIELD,
+    SAMSUNG_ENV_LEN_FIELD,
+    SERIAL_TTY_FIELD,
+    SNOWBALL_STARTUP_FILES_CONFIG_FIELD,
+    SOURCES_FIELD,
+    SPL_DD_FIELD,
+    SPL_FILE_FIELD,
+    SPL_IN_BOOT_PART_FIELD,
+    SPL_PACKAGE_FIELD,
+    SUPPORT_FIELD,
+    WIRED_INTERFACES_FIELD,
+    WIRELESS_INTERFACES_FIELD,
+    DEFINED_PARTITION_LAYOUTS,
+    VERSION_FIELD,
+)
+
 
 class HwpackConfigError(Exception):
     pass
@@ -35,81 +89,101 @@
 
 class Config(object):
     """Encapsulation of a hwpack-create configuration."""
+    translate_v2_to_v3 = {}
+    translate_v2_metadata = {}
 
     MAIN_SECTION = "hwpack"
-    NAME_KEY = "name"
     NAME_REGEX = r"[a-z0-9][a-z0-9+\-.]+$"
-    INCLUDE_DEBS_KEY = "include-debs"
-    SUPPORT_KEY = "support"
     SOURCES_ENTRY_KEY = "sources-entry"
-    PACKAGES_KEY = "packages"
     PACKAGE_REGEX = NAME_REGEX
     PATH_REGEX = r"\w[\w+\-./_]+$"
     GLOB_REGEX = r"[\w+\-./_\*]+$"
-    ORIGIN_KEY = "origin"
-    MAINTAINER_KEY = "maintainer"
-    ARCHITECTURES_KEY = "architectures"
+    INCLUDE_DEBS_KEY = "include-debs"
+    translate_v2_to_v3[INCLUDE_DEBS_KEY] = INCLUDE_DEBS_FIELD
+    translate_v2_metadata[ARCHITECTURES_FIELD] = "ARCHITECTURE"
     ASSUME_INSTALLED_KEY = "assume-installed"
+    translate_v2_to_v3[ASSUME_INSTALLED_KEY] = ASSUME_INSTALLED_FIELD
     U_BOOT_PACKAGE_KEY = "u_boot_package"
+    translate_v2_to_v3[U_BOOT_PACKAGE_KEY] = PACKAGE_FIELD
     U_BOOT_FILE_KEY = "u_boot_file"
+    translate_v2_to_v3[U_BOOT_FILE_KEY] = FILE_FIELD
+    translate_v2_metadata[U_BOOT_FILE_KEY] = "U_BOOT"
     SPL_FILE_KEY = "spl_file"
-    SERIAL_TTY_KEY = "serial_tty"
-    KERNEL_ADDR_KEY = "kernel_addr"
-    INITRD_ADDR_KEY = "initrd_addr"
-    LOAD_ADDR_KEY = "load_addr"
-    DTB_ADDR_KEY = "dtb_addr"
-    WIRED_INTERFACES_KEY = "wired_interfaces"
-    WIRELESS_INTERFACES_KEY = "wireless_interfaces"
-    PARTITION_LAYOUT_KEY = "partition_layout"
-    MMC_ID_KEY = "mmc_id"
-    FORMAT_KEY = "format"
-    BOOT_MIN_SIZE_KEY = "boot_min_size"
-    ROOT_MIN_SIZE_KEY = "root_min_size"
-    LOADER_MIN_SIZE_KEY = "loader_min_size"
-    LOADER_START_KEY = "loader_start"
-    SPL_PACKAGE_KEY = "spl_package"
-    VMLINUZ_KEY = "kernel_file"
-    INITRD_KEY = "initrd_file"
-    DTB_FILE_KEY = "dtb_file"
-    EXTRA_BOOT_OPTIONS_KEY = 'extra_boot_options'
-    BOOT_SCRIPT_KEY = 'boot_script'
+    translate_v2_metadata[SPL_FILE_KEY] = "SPL"
     UBOOT_IN_BOOT_PART_KEY = 'u_boot_in_boot_part'
+    translate_v2_to_v3[UBOOT_IN_BOOT_PART_KEY] = IN_BOOT_PART_FIELD
     UBOOT_DD_KEY = 'u_boot_dd'
-    SPL_IN_BOOT_PART_KEY = 'spl_in_boot_part'
-    SPL_DD_KEY = 'spl_dd'
-    ENV_DD_KEY = 'env_dd'
-    EXTRA_SERIAL_OPTS_KEY = 'extra_serial_options'
-    SNOWBALL_STARTUP_FILES_CONFIG_KEY = 'snowball_startup_files_config'
-    SAMSUNG_BL1_START_KEY = 'samsung_bl1_start'
-    SAMSUNG_BL1_LEN_KEY = 'samsung_bl1_len'
-    SAMSUNG_ENV_LEN_KEY = 'samsung_env_len'
-    SAMSUNG_BL2_LEN_KEY = 'samsung_bl2_len'
-
-    DEFINED_PARTITION_LAYOUTS = [
-        'bootfs16_rootfs',
-        'bootfs_rootfs',
-        'reserved_bootfs_rootfs',
-        ]
-
-    def __init__(self, fp):
+    translate_v2_to_v3[UBOOT_DD_KEY] = DD_FIELD
+
+    def __init__(self, fp, bootloader=None, board=None):
         """Create a Config.
 
         :param fp: a file-like object containing the configuration.
         """
+        obfuscated_e = None
+        obfuscated_yaml_e = ""
         try:
             self.parser = ConfigParser.RawConfigParser()
             self.parser.readfp(fp)
         except ConfigParser.Error, e:
             obfuscated_e = re.sub(r"([^ ]https://).+?(@)", r"\1***\2", str(e))
-            raise ConfigParser.Error(obfuscated_e)
+
+        if obfuscated_e:
+            # obfuscated_e being set indicates that something went wrong.
+            # It could be that the input is in fact YAML. Try the YAML
+            # parser.
+            try:
+                fp.seek(0)
+                self.parser = yaml.safe_load(fp)
+            except yaml.YAMLError, e:
+                obfuscated_yaml_e = re.sub(r"([^ ]https://).+?(@)",
+                                      r"\1***\2", str(e))
+            else:
+                # If YAML parsed OK, we don't have an error.
+                obfuscated_e = None
+                self.set_board(board)
+                self.set_bootloader(bootloader)
+
+        if obfuscated_e:
+            # If INI parsing from ConfigParser or YAML parsing failed,
+            # print both error messages.
+            msg = ("Failed to parse hardware pack configuration. Tried to "
+                   "parse as both INI and YAML. INI parsing error:\n" +
+                   obfuscated_e + "\n" +
+                   "YAML parser error:\n" +
+                   obfuscated_yaml_e)
+            raise ConfigParser.Error(msg)
+
+    def set_bootloader(self, bootloader):
+        """Set bootloader used to look up configuration in bootloader section.
+
+        If bootloader is None / empty and there is only one bootloader
+        available, use that.
+        """
+        if not bootloader:
+            # Auto-detect bootloader. If there is a single bootloader specified
+            # then use it, else, error.
+            bootloaders = self.bootloaders
+            if isinstance(bootloaders, dict):
+                # We have a list of bootloaders in the expected format
+                bootloaders = bootloaders.keys()
+                if len(bootloaders) == 1:
+                    bootloader = bootloaders[0]
+
+        self.bootloader = bootloader
+
+    def set_board(self, board):
+        """Set board used to look up per-board configuration"""
+        self.board = board
 
     def validate(self):
         """Check that this configuration follows the schema.
 
         :raises HwpackConfigError: if it does not.
         """
-        if not self.parser.has_section(self.MAIN_SECTION):
-            raise HwpackConfigError("No [%s] section" % self.MAIN_SECTION)
+        if isinstance(self.parser, ConfigParser.RawConfigParser):
+            if not self.parser.has_section(self.MAIN_SECTION):
+                raise HwpackConfigError("No [%s] section" % self.MAIN_SECTION)
         self._validate_format()
         self._validate_name()
         self._validate_include_debs()
@@ -153,88 +227,229 @@
             self._validate_samsung_env_len()
             self._validate_samsung_bl2_len()
 
-        self._validate_sections()
+        self._validate_sources()
 
     @property
     def format(self):
         """The format of the hardware pack. A subclass of HardwarePackFormat.
         """
-        try:
-            format_string = self.parser.get(self.MAIN_SECTION, self.FORMAT_KEY)
-        except ConfigParser.NoOptionError:
-            # Default to 1.0 to aviod breaking existing hwpack files.
-            # When this code no longer supports 1.0, it effectively makes
-            # explicitly specifying format in hwpack files mandatory.
-            format_string = "1.0"
+        if isinstance(self.parser, ConfigParser.RawConfigParser):
+            try:
+                format_string = self.parser.get(self.MAIN_SECTION,
+                                                FORMAT_FIELD)
+            except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+                # Default to 1.0 to aviod breaking existing hwpack files.
+                # When this code no longer supports 1.0, it effectively makes
+                # explicitly specifying format in hwpack files mandatory.
+                format_string = "1.0"
+        else:
+            format_string = self.parser.get(FORMAT_FIELD)
 
         if format_string == '1.0':
             return HardwarePackFormatV1()
         elif format_string == '2.0':
             return HardwarePackFormatV2()
+        elif format_string == 3.0 or format_string == '3.0':
+            return HardwarePackFormatV3()
         else:
-            raise HwpackConfigError("Format version '%s' is not supported." % \
-                                     format_string)
+            raise HwpackConfigError("Format version '%s' is not supported." %
+                                        format_string)
 
     @property
     def name(self):
         """The name of the hardware pack. A str."""
-        return self.parser.get(self.MAIN_SECTION, self.NAME_KEY)
+        return self._get_option(NAME_FIELD)
+
+    @property
+    def version(self):
+        return self._get_option(VERSION_FIELD)
 
     @property
     def include_debs(self):
         """Whether the hardware pack should contain .debs. A bool."""
         try:
-            if not self.parser.get(
-                self.MAIN_SECTION, self.INCLUDE_DEBS_KEY):
+            if self._get_option(self.INCLUDE_DEBS_KEY) == None:
                 return True
-            return self.parser.getboolean(
-                self.MAIN_SECTION, self.INCLUDE_DEBS_KEY)
+            try:
+                return self._get_option_bool(self.INCLUDE_DEBS_KEY)
+            except ValueError as e:
+                raise HwpackConfigError("Invalid value for include-debs: %s" %
+                                        e)
         except ConfigParser.NoOptionError:
             return True
 
     @property
+    def bootloaders(self):
+        """Bootloaders available in the hardware pack"""
+        return self._get_option(BOOTLOADERS_FIELD)
+
+    @property
     def uboot_in_boot_part(self):
         """Whether uboot binary should be put in the boot partition. A str."""
-        return self.parser.get(self.MAIN_SECTION, self.UBOOT_IN_BOOT_PART_KEY)
+        return self._get_bootloader_option(self.UBOOT_IN_BOOT_PART_KEY)
 
     @property
     def uboot_dd(self):
         """If the uboot binary should be dd:d to the boot partition
         this field specifies the offset. An int."""
-        return self._get_option_from_main_section(self.UBOOT_DD_KEY)
+        return self._get_bootloader_option(self.UBOOT_DD_KEY)
 
     @property
     def spl_in_boot_part(self):
         """Whether spl binary should be put in the boot partition. A str."""
-        return self._get_option_from_main_section(self.SPL_IN_BOOT_PART_KEY)
+        return self._get_bootloader_option(SPL_IN_BOOT_PART_FIELD)
 
     @property
     def spl_dd(self):
         """If the spl binary should be dd:d to the boot partition
         this field specifies the offset. An int."""
-        return self._get_option_from_main_section(self.SPL_DD_KEY)
+        return self._get_bootloader_option(SPL_DD_FIELD)
 
     @property
     def env_dd(self):
         """If the env should be dd:d to the boot partition. 'Yes' or 'No'."""
-        return self._get_option_from_main_section(self.ENV_DD_KEY)
-
-    def _get_option_from_main_section(self, key):
-        """Get the value from the main section for the given key.
+        return self._get_bootloader_option(ENV_DD_FIELD)
+
+    def _get_option_bool(self, key):
+        """Gets a boolean value from the key."""
+        if self.format.format_as_string == '3.0':
+            value = self._get_option(key, convert_to="disable")
+            if isinstance(value, bool):
+                return value
+            else:
+                raise ValueError(value)
+        else:
+            try:
+                return self.parser.getboolean(self.MAIN_SECTION, key)
+            except ConfigParser.NoOptionError:
+                return None
+
+    def _get_bootloader_option(self, key, join_list_with=False,
+                               convert_to=None):
+        """Get an option inside the current bootloader section."""
+        if self._is_v3:
+            if not self.bootloader:
+                raise ValueError("bootloader not set.")
+            if not isinstance(key, list):
+                keys = [key]
+            keys = [BOOTLOADERS_FIELD, self.bootloader] + keys
+        else:
+            keys = key
+
+        return self._get_option(keys, join_list_with, convert_to)
+
+    def _bool_to_string(self, value):
+        """Convert value, treated as boolean, to string "yes" or "no"."""
+        if value:
+            return "yes"
+        else:
+            return "no"
+
+    def _hex_addrress(self, value):
+        """Convert value to 8 character hex string"""
+        converted_value = value
+        if not isinstance(value, str):
+            converted_value = "0x%08x" % value
+        return converted_value
+
+    def _v2_key_to_v3(self, key):
+        """Convert V2 key to a V3 key"""
+        if key in self.translate_v2_to_v3:
+            key = self.translate_v2_to_v3[key]
+        return key
+
+    def _get_v3_option(self, keys):
+        """Find value in config dictionary based on supplied list (keys)."""
+        result = self.parser
+        for key in keys:
+            key = self._v2_key_to_v3(key)
+            if result is not None:
+                result = result.get(key, None)
+        return result
+
+    def get_option(self, name):
+        """Return the value of an attribute by name.
+
+        Used when you can't use a property.
+        """
+        return attrgetter(name)(self)
+
+    def _get_option(self, key, join_list_with=False, convert_to=None):
+        """Return value for the given key. Precedence to board specific values.
 
         :param key: the key to return the value for.
         :type key: str.
+        :param join_list_with: Used to convert lists to strings.
+        :type join_list_with: str
+        :param convert_to: Used to convert stored value to another type.
+        :type convert_to: type or function.
         :return: the value for that key, or None if the key is not present
             or the value is empty.
         :rtype: str or None.
         """
-        try:
-            result = self.parser.get(self.MAIN_SECTION, key)
+        if self.format.format_as_string == "3.0":
+            if not isinstance(key, list):
+                keys = [key]
+            else:
+                keys = key
+
+            result = None  # Just mark result as not set yet...
+
+            # If board is set, search board specific keys first
+            if self.board:
+                result = self._get_v3_option([BOARDS_FIELD, self.board] + keys)
+
+            # If a board specific value isn't found, look for a global one
+            if result == None:
+                result = self._get_v3_option(keys)
+
+            # If no value is found, bail early (return None)
+            if result == None:
+                return None
+
+            # <v3 compatibility: Lists of items can be converted to strings
+            if join_list_with and isinstance(result, list):
+                result = join_list_with.join(result)
+
+            # <v3 compatibility:
+            # To aid code that is trying to keep the format of results the
+            # same as before, we have some type conversions. By default
+            # booleans are "yes" or "no", integers are converted to
+            # strings.
+            if not convert_to:
+                if isinstance(result, int):
+                    if isinstance(result, bool):
+                        convert_to = self._bool_to_string
+                    else:
+                        convert_to = str
+
+            if convert_to and convert_to != "disable":
+                if isinstance(result, list):
+                    new_list = []
+                    for item in result:
+                        new_list = convert_to(item)
+                    result = new_list
+                else:
+                    result = convert_to(result)
+        else:
+            try:
+                result = self.parser.get(self.MAIN_SECTION, key)
+            except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+                # May be trying to read a metadata file, which has uppercase
+                # keys, some of which need translating to different strings...
+                if key in self.translate_v2_metadata:
+                    key = self.translate_v2_metadata[key]
+                else:
+                    key = key.upper()
+                try:
+                    result = self.parser.get(self.MAIN_SECTION, key)
+                except (ConfigParser.NoOptionError,
+                        ConfigParser.NoSectionError):
+                    result = None
             if not result:
-                return None
-            return result
-        except ConfigParser.NoOptionError:
-            return None
+                result = None
+
+        return result
 
     @property
     def serial_tty(self):
@@ -242,7 +457,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.SERIAL_TTY_KEY)
+        return self._get_option(SERIAL_TTY_FIELD)
 
     @property
     def extra_boot_options(self):
@@ -250,7 +465,8 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.EXTRA_BOOT_OPTIONS_KEY)
+        return self._get_bootloader_option(EXTRA_BOOT_OPTIONS_FIELD,
+                                           join_list_with=" ")
 
     @property
     def extra_serial_opts(self):
@@ -258,7 +474,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.EXTRA_SERIAL_OPTS_KEY)
+        return self._get_option(EXTRA_SERIAL_OPTIONS_FIELD, join_list_with=" ")
 
     @property
     def boot_script(self):
@@ -266,7 +482,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.BOOT_SCRIPT_KEY)
+        return self._get_option(BOOT_SCRIPT_FIELD)
 
     @property
     def snowball_startup_files_config(self):
@@ -274,8 +490,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(
-            self.SNOWBALL_STARTUP_FILES_CONFIG_KEY)
+        return self._get_option(SNOWBALL_STARTUP_FILES_CONFIG_FIELD)
 
     @property
     def kernel_addr(self):
@@ -283,7 +498,8 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.KERNEL_ADDR_KEY)
+        return self._get_option(KERNEL_ADDR_FIELD,
+                                convert_to=self._hex_addrress)
 
     @property
     def initrd_addr(self):
@@ -291,7 +507,8 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.INITRD_ADDR_KEY)
+        return self._get_option(INITRD_ADDR_FIELD,
+                                convert_to=self._hex_addrress)
 
     @property
     def load_addr(self):
@@ -299,7 +516,7 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.LOAD_ADDR_KEY)
+        return self._get_option(LOAD_ADDR_FIELD, convert_to=self._hex_addrress)
 
     @property
     def dtb_addr(self):
@@ -307,7 +524,7 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.DTB_ADDR_KEY)
+        return self._get_option(DTB_ADDR_FIELD, convert_to=self._hex_addrress)
 
     @property
     def wired_interfaces(self):
@@ -315,7 +532,7 @@
 
         A list of str.
         """
-        return self._get_list_from_main_section(self.WIRED_INTERFACES_KEY)
+        return self._get_list(WIRED_INTERFACES_FIELD)
 
     @property
     def wireless_interfaces(self):
@@ -323,7 +540,7 @@
 
         A list of str.
         """
-        return self._get_list_from_main_section(self.WIRELESS_INTERFACES_KEY)
+        return self._get_list(WIRELESS_INTERFACES_FIELD)
 
     @property
     def partition_layout(self):
@@ -333,7 +550,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.PARTITION_LAYOUT_KEY)
+        return self._get_option(PARTITION_LAYOUT_FIELD, join_list_with=" ")
 
     @property
     def mmc_id(self):
@@ -341,7 +558,7 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.MMC_ID_KEY)
+        return self._get_option(MMC_ID_FIELD)
 
     @property
     def root_min_size(self):
@@ -349,7 +566,7 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.ROOT_MIN_SIZE_KEY)
+        return self._get_option(ROOT_MIN_SIZE_FIELD)
 
     @property
     def boot_min_size(self):
@@ -357,7 +574,7 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.BOOT_MIN_SIZE_KEY)
+        return self._get_option(BOOT_MIN_SIZE_FIELD)
 
     @property
     def loader_min_size(self):
@@ -365,7 +582,7 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.LOADER_MIN_SIZE_KEY)
+        return self._get_option(LOADER_MIN_SIZE_FIELD)
 
     @property
     def loader_start(self):
@@ -373,7 +590,7 @@
 
         An int.
         """
-        return self._get_option_from_main_section(self.LOADER_START_KEY)
+        return self._get_option(LOADER_START_FIELD)
 
     @property
     def origin(self):
@@ -381,7 +598,7 @@
 
         A str or None if no origin should be recorded.
         """
-        return self._get_option_from_main_section(self.ORIGIN_KEY)
+        return self._get_option(ORIGIN_FIELD)
 
     @property
     def maintainer(self):
@@ -389,7 +606,7 @@
 
         A str or None if not maintainer should be recorded.
         """
-        return self._get_option_from_main_section(self.MAINTAINER_KEY)
+        return self._get_option(MAINTAINER_FIELD)
 
     @property
     def support(self):
@@ -397,17 +614,21 @@
 
         A str or None if no support level should be recorded.
         """
-        return self._get_option_from_main_section(self.SUPPORT_KEY)
+        return self._get_option(SUPPORT_FIELD)
 
-    def _get_list_from_main_section(self, key):
-        raw_values = self._get_option_from_main_section(key)
-        if raw_values is None:
+    def _get_list(self, key):
+        values = self._get_option(key)
+        if values is None:
             return []
-        values = re.split("\s+", raw_values)
+
+        if not isinstance(values, list):
+            values = re.split("\s+", values)
+
         filtered_values = []
         for value in values:
             if value not in filtered_values:
                 filtered_values.append(value)
+
         return filtered_values
 
     @property
@@ -416,7 +637,7 @@
 
         A list of str.
         """
-        return self._get_list_from_main_section(self.PACKAGES_KEY)
+        return self._get_list(PACKAGES_FIELD)
 
     @property
     def u_boot_package(self):
@@ -424,7 +645,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.U_BOOT_PACKAGE_KEY)
+        return self._get_bootloader_option(self.U_BOOT_PACKAGE_KEY)
 
     @property
     def u_boot_file(self):
@@ -432,7 +653,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.U_BOOT_FILE_KEY)
+        return self._get_bootloader_option(self.U_BOOT_FILE_KEY)
 
     @property
     def spl_file(self):
@@ -440,7 +661,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.SPL_FILE_KEY)
+        return self._get_bootloader_option(SPL_FILE_FIELD)
 
     @property
     def spl_package(self):
@@ -448,7 +669,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.SPL_PACKAGE_KEY)
+        return self._get_bootloader_option(SPL_PACKAGE_FIELD)
 
     @property
     def vmlinuz(self):
@@ -456,7 +677,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.VMLINUZ_KEY)
+        return self._get_option(KERNEL_FILE_FIELD)
 
     @property
     def initrd(self):
@@ -464,7 +685,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.INITRD_KEY)
+        return self._get_option(INITRD_FILE_FIELD)
 
     @property
     def dtb_file(self):
@@ -472,7 +693,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.DTB_FILE_KEY)
+        return self._get_option(DTB_FILE_FIELD)
 
     @property
     def samsung_bl1_start(self):
@@ -480,7 +701,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.SAMSUNG_BL1_START_KEY)
+        return self._get_option(SAMSUNG_BL1_START_FIELD)
 
     @property
     def samsung_bl1_len(self):
@@ -488,7 +709,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.SAMSUNG_BL1_LEN_KEY)
+        return self._get_option(SAMSUNG_BL1_LEN_FIELD)
 
     @property
     def samsung_env_len(self):
@@ -496,7 +717,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.SAMSUNG_ENV_LEN_KEY)
+        return self._get_option(SAMSUNG_ENV_LEN_FIELD)
 
     @property
     def samsung_bl2_len(self):
@@ -504,7 +725,7 @@
 
         A str.
         """
-        return self._get_option_from_main_section(self.SAMSUNG_BL2_LEN_KEY)
+        return self._get_option(SAMSUNG_BL2_LEN_FIELD)
 
     @property
     def architectures(self):
@@ -512,7 +733,15 @@
 
         A list of str.
         """
-        return self._get_list_from_main_section(self.ARCHITECTURES_KEY)
+        return self._get_list(ARCHITECTURES_FIELD)
+
+    @property
+    def architecture(self):
+        """The architectures to build the hwpack for.
+
+        A list of str.
+        """
+        return self._get_option(ARCHITECTURE_FIELD)
 
     @property
     def assume_installed(self):
@@ -520,7 +749,7 @@
 
         A list of str.
         """
-        return self._get_list_from_main_section(self.ASSUME_INSTALLED_KEY)
+        return self._get_list(self.ASSUME_INSTALLED_KEY)
 
     @property
     def sources(self):
@@ -528,13 +757,16 @@
 
         A dict mapping source identifiers to sources entries.
         """
-        sources = {}
-        sections = self.parser.sections()
-        for section_name in sections:
-            if section_name == self.MAIN_SECTION:
-                continue
-            sources[section_name] = self.parser.get(
-                section_name, self.SOURCES_ENTRY_KEY)
+        if self._is_v3:
+            sources = self.parser.get(SOURCES_FIELD)
+        else:
+            sources = {}
+            sections = self.parser.sections()
+            for section_name in sections:
+                if section_name == self.MAIN_SECTION:
+                    continue
+                sources[section_name] = self.parser.get(
+                    section_name, self.SOURCES_ENTRY_KEY)
         return sources
 
     def _validate_format(self):
@@ -575,19 +807,25 @@
     def _validate_vmlinuz(self):
         vmlinuz = self.vmlinuz
         if not vmlinuz:
-            raise HwpackConfigError("No kernel_file in the [%s] section" % \
-                                        self.MAIN_SECTION)
+            raise HwpackConfigError(self._not_found_message(KERNEL_FILE_FIELD))
         self._assert_matches_pattern(
             self.GLOB_REGEX, vmlinuz, "Invalid path: %s" % vmlinuz)
 
     def _validate_initrd(self):
         initrd = self.initrd
         if not initrd:
-            raise HwpackConfigError("No initrd_file in the [%s] section" % \
-                                        self.MAIN_SECTION)
+            raise HwpackConfigError(self._not_found_message(INITRD_FILE_FIELD))
         self._assert_matches_pattern(
             self.GLOB_REGEX, initrd, "Invalid path: %s" % initrd)
 
+    def _not_found_message(self, thing, v2_section=None):
+        if self._is_v3:
+            return "No " + thing + " found in the metadata"
+        else:
+            if not v2_section:
+                v2_section = self.MAIN_SECTION
+            return "No " + thing + " in the [" + v2_section + "] section"
+
     def _validate_dtb_file(self):
         dtb_file = self.dtb_file
         if dtb_file is not None:
@@ -624,36 +862,40 @@
         if len(serial_tty) < 4 or serial_tty[:3] != 'tty':
             raise HwpackConfigError("Invalid serial tty: %s" % serial_tty)
 
-    def _validate_addr(self, addr):
-        return re.match(r"^0x[a-fA-F0-9]{8}$", addr)
+    def _validate_addr(self, key):
+        """Validate the address for the given key.
+        Assumptions:
+            1. key name is of the form name_addr
+            2. property name matches key name
+
+        Currently these assumptions are met and it seems reasonable to place
+        these restrictions on future code.
+        """
+        name = re.sub("_addr", "", key)
+
+        try:
+            addr = attrgetter(key)(self)
+        except TypeError:
+            raise HwpackConfigError("Invalid %s address: %s" %
+                                    (name, self._get_option(key)))
+
+        if addr == None:
+            return
+
+        if not re.match(r"^0x[a-fA-F0-9]{8}$", addr):
+            raise HwpackConfigError("Invalid %s address: %s" % (name, addr))
 
     def _validate_kernel_addr(self):
-        addr = self.kernel_addr
-        if addr is None:
-            return
-        if not self._validate_addr(addr):
-            raise HwpackConfigError("Invalid kernel address: %s" % addr)
+        self._validate_addr(KERNEL_ADDR_FIELD)
 
     def _validate_initrd_addr(self):
-        addr = self.initrd_addr
-        if addr is None:
-            return
-        if not self._validate_addr(addr):
-            raise HwpackConfigError("Invalid initrd address: %s" % addr)
+        self._validate_addr(INITRD_ADDR_FIELD)
 
     def _validate_load_addr(self):
-        addr = self.load_addr
-        if addr is None:
-            return
-        if not self._validate_addr(addr):
-            raise HwpackConfigError("Invalid load address: %s" % addr)
+        self._validate_addr(LOAD_ADDR_FIELD)
 
     def _validate_dtb_addr(self):
-        addr = self.dtb_addr
-        if addr is None:
-            return
-        if not self._validate_addr(addr):
-            raise HwpackConfigError("Invalid dtb address: %s" % addr)
+        self._validate_addr(DTB_ADDR_FIELD)
 
     def _validate_wired_interfaces(self):
         pass
@@ -662,12 +904,19 @@
         pass
 
     def _validate_partition_layout(self):
-        if self.partition_layout not in self.DEFINED_PARTITION_LAYOUTS:
-            raise HwpackConfigError(
-                "Undefined partition layout %s in the [%s] section. "
-                "Valid partition layouts are %s."
-                % (self.partition_layout, self.MAIN_SECTION,
-                   ", ".join(self.DEFINED_PARTITION_LAYOUTS)))
+        if self.partition_layout not in DEFINED_PARTITION_LAYOUTS:
+            if self._is_v3:
+                message = ("Undefined partition layout %s. "
+                           "Valid partition layouts are %s." %
+                           (self.partition_layout,
+                            ", ".join(DEFINED_PARTITION_LAYOUTS)))
+            else:
+                message = ("Undefined partition layout %s in the [%s] section."
+                           " Valid partition layouts are %s." %
+                           (self.partition_layout, self.MAIN_SECTION,
+                            ", ".join(DEFINED_PARTITION_LAYOUTS)))
+
+            raise HwpackConfigError(message)
 
     def _validate_mmc_id(self):
         mmc_id = self.mmc_id
@@ -725,14 +974,25 @@
         except ValueError:
             raise HwpackConfigError(
                 "Invalid value for include-debs: %s"
-                % self.parser.get("hwpack", "include-debs"))
+                % self.include_debs)
+
+    @property
+    def _is_v3(self):
+        """Checks if format is 3.0."""
+        return self.format.format_as_string == '3.0'
+
+    def _validate_bool(self, value):
+        """Checks if a value is boolean or not, represented by "yes" or "no".
+        """
+        if not isinstance(value, str):
+            return False
+        return string.lower(value) in ['yes', 'no']
 
     def _validate_uboot_in_boot_part(self):
-        uboot_in_boot_part = self.uboot_in_boot_part
-        if string.lower(uboot_in_boot_part) not in ['yes', 'no']:
+        if not self._validate_bool(self.uboot_in_boot_part):
             raise HwpackConfigError(
                 "Invalid value for u_boot_in_boot_part: %s"
-                % self.parser.get("hwpack", "u_boot_in_boot_part"))
+                % self.uboot_in_boot_part)
 
     def _validate_spl_in_boot_part(self):
         spl_in_boot_part = self.spl_in_boot_part
@@ -741,7 +1001,7 @@
         if string.lower(spl_in_boot_part) not in ['yes', 'no']:
             raise HwpackConfigError(
                 "Invalid value for spl_in_boot_part: %s"
-                % self.parser.get("hwpack", "spl_in_boot_part"))
+                % self.spl_in_boot_part)
 
     def _validate_env_dd(self):
         env_dd = self.env_dd
@@ -750,7 +1010,7 @@
         if string.lower(env_dd) not in ['yes', 'no']:
             raise HwpackConfigError(
                 "Invalid value for env_dd: %s"
-                % self.parser.get("hwpack", "env_dd"))
+                % self.env_dd)
 
     def _validate_uboot_dd(self):
         uboot_dd = self.uboot_dd
@@ -778,33 +1038,40 @@
             raise HwpackConfigError(
                 "Invalid value for support: %s" % support)
 
+    def _invalid_package_message(self, package_name, section_name, value):
+        if self._is_v3:
+            message = ("Invalid value in %s in the metadata: %s" %
+                       (package_name, value))
+        else:
+            message = ("Invalid value in %s in the [%s] section: %s" %
+                       (package_name, section_name, value))
+        return message
+
     def _validate_packages(self):
         packages = self.packages
         if not packages:
-            raise HwpackConfigError(
-                "No %s in the [%s] section"
-                % (self.PACKAGES_KEY, self.MAIN_SECTION))
+            raise HwpackConfigError(self._not_found_message(PACKAGES_FIELD))
         for package in packages:
             self._assert_matches_pattern(
-                self.PACKAGE_REGEX, package, "Invalid value in %s in the " \
-                    "[%s] section: %s" % (self.PACKAGES_KEY, self.MAIN_SECTION,
-                                          package))
+                self.PACKAGE_REGEX, package,
+                self._invalid_package_message(
+                    PACKAGES_FIELD, self.MAIN_SECTION, package))
 
     def _validate_u_boot_package(self):
         u_boot_package = self.u_boot_package
         if u_boot_package is not None:
             self._assert_matches_pattern(
-                self.PACKAGE_REGEX, u_boot_package, "Invalid value in %s in " \
-                    "the [%s] section: %s" % (
-                        self.U_BOOT_PACKAGE_KEY, self.MAIN_SECTION,
-                        u_boot_package))
+                self.PACKAGE_REGEX, u_boot_package,
+                self._invalid_package_message(
+                    self.U_BOOT_PACKAGE_KEY, self.MAIN_SECTION,
+                    u_boot_package))
 
     def _validate_spl_package(self):
         spl_package = self.spl_package
         if spl_package is not None:
             self._assert_matches_pattern(
-                self.PACKAGE_REGEX, spl_package, "Invalid value in %s in " \
-                    "the [%s] section: %s" % (self.SPL_PACKAGE_KEY,
+                self.PACKAGE_REGEX, spl_package,
+                self._invalid_package_message(SPL_PACKAGE_FIELD,
                                               self.MAIN_SECTION,
                                               spl_package))
 
@@ -852,48 +1119,66 @@
         architectures = self.architectures
         if not architectures:
             raise HwpackConfigError(
-                "No %s in the [%s] section"
-                % (self.ARCHITECTURES_KEY, self.MAIN_SECTION))
+                self._not_found_message(ARCHITECTURES_FIELD))
 
     def _validate_assume_installed(self):
         assume_installed = self.assume_installed
         for package in assume_installed:
             self._assert_matches_pattern(
-                self.PACKAGE_REGEX, package, "Invalid value in %s in the " \
-                    "[%s] section: %s" % (self.ASSUME_INSTALLED_KEY,
-                                          self.MAIN_SECTION, package))
-
-    def _validate_section_sources_entry(self, section_name):
-        try:
-            sources_entry = self.parser.get(
-                section_name, self.SOURCES_ENTRY_KEY)
-            if not sources_entry:
+                self.PACKAGE_REGEX, package,
+                self._invalid_package_message(self.ASSUME_INSTALLED_KEY,
+                                              self.MAIN_SECTION, package))
+
+    def _message_start(self, key, section_name):
+        if self._is_v3:
+            message = "The %s, %s " % (key, section_name)
+        else:
+            message = "The %s in the [%s] section " % (key, section_name)
+        return message
+
+    def _validate_source(self, section_name):
+        if self._is_v3:
+            sources_entry = self._get_option([SOURCES_FIELD] + [section_name])
+        else:
+            try:
+                sources_entry = self.parser.get(
+                    section_name, self.SOURCES_ENTRY_KEY)
+            except ConfigParser.NoOptionError:
+                raise HwpackConfigError(
+                    "No %s in the [%s] section"
+                    % (self.SOURCES_ENTRY_KEY, section_name))
+
+        if not sources_entry:
+            raise HwpackConfigError(
+                self._message_start(self.SOURCES_ENTRY_KEY, section_name) +
+                "is missing the URI")
+        if len(sources_entry.split(" ", 1)) < 2:
+            raise HwpackConfigError(
+                self._message_start(self.SOURCES_ENTRY_KEY, section_name) +
+                "is missing the distribution")
+        if sources_entry.startswith("deb"):
+            raise HwpackConfigError(
+                self._message_start(self.SOURCES_ENTRY_KEY, section_name) +
+                "shouldn't start with 'deb'")
+
+    def _validate_sources(self):
+        if self._is_v3:
+            source_dict = self.parser.get(SOURCES_FIELD)
+            if not source_dict:
+                return
+            if isinstance(source_dict, dict):
+                sources = source_dict.keys()
+            else:
                 raise HwpackConfigError(
                     "The %s in the [%s] section is missing the URI"
-                    % (self.SOURCES_ENTRY_KEY, section_name))
-            if len(sources_entry.split(" ", 1)) < 2:
-                raise HwpackConfigError(
-                    "The %s in the [%s] section is missing the distribution"
-                    % (self.SOURCES_ENTRY_KEY, section_name))
-            if sources_entry.startswith("deb"):
-                raise HwpackConfigError(
-                    "The %s in the [%s] section shouldn't start with 'deb'"
-                    % (self.SOURCES_ENTRY_KEY, section_name))
-        except ConfigParser.NoOptionError:
-            raise HwpackConfigError(
-                "No %s in the [%s] section"
-                % (self.SOURCES_ENTRY_KEY, section_name))
-
-    def _validate_section(self, section_name):
-        self._validate_section_sources_entry(section_name)
-
-    def _validate_sections(self):
-        sections = self.parser.sections()
+                    % (self.SOURCES_ENTRY_KEY, source_dict))
+        else:
+            sources = self.parser.sections()
         found = False
-        for section_name in sections:
-            if section_name == self.MAIN_SECTION:
+        for source_name in sources:
+            if source_name == self.MAIN_SECTION:
                 continue
-            self._validate_section(section_name)
+            self._validate_source(source_name)
             found = True
         if not found:
             raise HwpackConfigError(

=== modified file 'linaro_image_tools/hwpack/hardwarepack.py'
--- linaro_image_tools/hwpack/hardwarepack.py	2012-06-13 14:49:14 +0000
+++ linaro_image_tools/hwpack/hardwarepack.py	2012-07-23 12:04:44 +0000
@@ -32,6 +32,45 @@
 from linaro_image_tools.hwpack.hardwarepack_format import (
     HardwarePackFormatV1,
 )
+from linaro_image_tools.hwpack.hwpack_convert import (
+    dump,
+)
+
+from hwpack_fields import (
+    BOOTLOADERS_FIELD,
+    BOOT_MIN_SIZE_FIELD,
+    BOOT_SCRIPT_FIELD,
+    DTB_ADDR_FIELD,
+    DTB_FILE_FIELD,
+    EXTRA_SERIAL_OPTIONS_FIELD,
+    FILE_FIELD,
+    FORMAT_FIELD,
+    INITRD_ADDR_FIELD,
+    INITRD_FILE_FIELD,
+    KERNEL_ADDR_FIELD,
+    KERNEL_FILE_FIELD,
+    LOAD_ADDR_FIELD,
+    LOADER_MIN_SIZE_FIELD,
+    LOADER_START_FIELD,
+    MAINTAINER_FIELD,
+    METADATA_ARCH_FIELD,
+    METADATA_VERSION_FIELD,
+    MMC_ID_FIELD,
+    NAME_FIELD,
+    ORIGIN_FIELD,
+    PARTITION_LAYOUT_FIELD,
+    ROOT_MIN_SIZE_FIELD,
+    SAMSUNG_BL1_LEN_FIELD,
+    SAMSUNG_BL1_START_FIELD,
+    SAMSUNG_BL2_LEN_FIELD,
+    SAMSUNG_ENV_LEN_FIELD,
+    SERIAL_TTY_FIELD,
+    SNOWBALL_STARTUP_FILES_CONFIG_FIELD,
+    SPL_FILE_FIELD,
+    SUPPORT_FIELD,
+    WIRED_INTERFACES_FIELD,
+    WIRELESS_INTERFACES_FIELD,
+)
 
 
 class Metadata(object):
@@ -127,6 +166,13 @@
         self.samsung_bl2_len = samsung_bl2_len
 
     @classmethod
+    def add_v3_config(self, bootloaders):
+        """Add fields that are specific to the v3 config format.
+
+        :param bootloaders: The bootloaders section of the hwpack."""
+        self.bootloaders = bootloaders
+
+    @classmethod
     def from_config(cls, config, version, architecture):
         """Create a Metadata from a Config object.
 
@@ -153,39 +199,163 @@
             # Helper variable to adhere to the line length limit.
             snowball_startup_config = config.snowball_startup_files_config
             metadata.add_v2_config(
-                serial_tty=config.serial_tty,
+                boot_min_size=config.boot_min_size,
+                boot_script=config.boot_script,
+                dtb_addr=config.dtb_addr,
+                dtb_file=config.dtb_file,
+                env_dd=config.env_dd,
+                extra_boot_options=config.extra_boot_options,
+                extra_serial_opts=config.extra_serial_opts,
+                initrd_addr=config.initrd_addr,
+                initrd=config.initrd,
                 kernel_addr=config.kernel_addr,
-                initrd_addr=config.initrd_addr,
                 load_addr=config.load_addr,
-                wired_interfaces=config.wired_interfaces,
-                wireless_interfaces=config.wireless_interfaces,
+                loader_min_size=config.loader_min_size,
+                loader_start=config.loader_start,
+                mmc_id=config.mmc_id,
                 partition_layout=config.partition_layout,
-                mmc_id=config.mmc_id,
-                boot_min_size=config.boot_min_size,
                 root_min_size=config.root_min_size,
-                loader_min_size=config.loader_min_size,
-                loader_start=config.loader_start,
-                vmlinuz=config.vmlinuz,
-                initrd=config.initrd,
-                dtb_file=config.dtb_file,
-                dtb_addr=config.dtb_addr,
-                extra_boot_options=config.extra_boot_options,
-                boot_script=config.boot_script,
-                uboot_in_boot_part=config.uboot_in_boot_part,
-                uboot_dd=config.uboot_dd,
-                spl_in_boot_part=config.spl_in_boot_part,
-                spl_dd=config.spl_dd,
-                env_dd=config.env_dd,
-                extra_serial_opts=config.extra_serial_opts,
-                snowball_startup_files_config=snowball_startup_config,
+                samsung_bl1_len=config.samsung_bl1_len,
                 samsung_bl1_start=config.samsung_bl1_start,
-                samsung_bl1_len=config.samsung_bl1_len,
+                samsung_bl2_len=config.samsung_bl2_len,
                 samsung_env_len=config.samsung_env_len,
-                samsung_bl2_len=config.samsung_bl2_len)
+                serial_tty=config.serial_tty,
+                snowball_startup_files_config=snowball_startup_config,
+                spl_dd=config.spl_dd,
+                spl_in_boot_part=config.spl_in_boot_part,
+                uboot_dd=config.uboot_dd,
+                uboot_in_boot_part=config.uboot_in_boot_part,
+                vmlinuz=config.vmlinuz,
+                wired_interfaces=config.wired_interfaces,
+                wireless_interfaces=config.wireless_interfaces,
+                )
+        if config.format.format_as_string == '3.0':
+            metadata.add_v3_config(config.bootloaders)
         return metadata
 
     def __str__(self):
-        """Get the contents of the metadata file."""
+        if self.format.format_as_string == '3.0':
+            return self.create_metadata_new()
+        else:
+            return self.create_metadata_old()
+
+    def set_config_value(self, dictionary, search_key, new_value):
+        """Loop recursively through a dictionary looking for the specified key
+        substituting its value.
+        In a metadata file, at least two fields from the Config class have
+        their value calculated during the build phase of the hardware pack.
+        Here those known fiels will be updated with the newly calculated
+        values.
+
+        :param dictionary: The dictionary to loop through.
+        :param search_key: The key to search.
+        :param new_value: The new value for the key.
+        """
+        for key, value in dictionary.iteritems():
+            if key == search_key:
+                dictionary[key] = new_value
+                break
+            elif isinstance(value, dict):
+                self.set_config_value(value, search_key, new_value)
+
+    def create_metadata_new(self):
+        """Get the contents of the metadata file.
+
+        The metadata file is almost an identical copy of the hwpack
+        configuration file. Only a couple of fields are different, and some
+        are missing.
+
+        :return A string.
+        """
+        metadata = ""
+        metadata += dump({FORMAT_FIELD: self.format.format_as_string})
+        metadata += dump({NAME_FIELD: self.name})
+        metadata += dump({METADATA_VERSION_FIELD: self.version})
+        # This is a single 'architecture' hwpack, each arch will get its own,
+        # it is not retrieved from the Config.
+        metadata += dump({METADATA_ARCH_FIELD: self.architecture})
+        if self.origin is not None:
+            metadata += dump({ORIGIN_FIELD: self.origin})
+        if self.maintainer is not None:
+            metadata += dump({MAINTAINER_FIELD: self.maintainer})
+        if self.support is not None:
+            metadata += dump({SUPPORT_FIELD: self.support})
+        if self.bootloaders is not None:
+            # XXX We need to do this, since some necessary values are set
+            # when the hwpack archive is built, and are not in the hwpack
+            # config. Since we know which are the keys we have to look for,
+            # we just loop through all of them.
+            if self.spl is not None:
+                self.set_config_value(self.bootloaders, SPL_FILE_FIELD,
+                                               self.spl)
+            if self.u_boot is not None:
+                self.set_config_value(self.bootloaders, FILE_FIELD,
+                                                self.u_boot)
+            metadata += dump({BOOTLOADERS_FIELD: self.bootloaders})
+        if self.serial_tty is not None:
+            metadata += dump({SERIAL_TTY_FIELD: self.serial_tty})
+        if self.kernel_addr is not None:
+            metadata += dump({KERNEL_ADDR_FIELD: self.kernel_addr})
+        if self.initrd_addr is not None:
+            metadata += dump({INITRD_ADDR_FIELD: self.initrd_addr})
+        if self.load_addr is not None:
+            metadata += dump({LOAD_ADDR_FIELD: self.load_addr})
+        if self.dtb_addr is not None:
+            metadata += dump({DTB_ADDR_FIELD: self.dtb_addr})
+        if self.wired_interfaces != []:
+            eth_interfaces = " ".join(self.wired_interfaces)
+            metadata += dump({WIRED_INTERFACES_FIELD: eth_interfaces})
+        if self.wireless_interfaces != []:
+            wifi_interfaces = " ".join(self.wireless_interfaces)
+            metadata += dump({WIRELESS_INTERFACES_FIELD: wifi_interfaces})
+        if self.partition_layout is not None:
+            metadata += dump({PARTITION_LAYOUT_FIELD: self.partition_layout})
+        if self.mmc_id is not None:
+            metadata += dump({MMC_ID_FIELD: self.mmc_id})
+        if self.boot_min_size is not None:
+            metadata += dump({BOOT_MIN_SIZE_FIELD: self.boot_min_size})
+        if self.root_min_size is not None:
+            metadata += dump({ROOT_MIN_SIZE_FIELD: self.root_min_size})
+        if self.loader_min_size is not None:
+            metadata += dump({LOADER_MIN_SIZE_FIELD: self.loader_min_size})
+        if self.loader_start is not None:
+            metadata += dump({LOADER_START_FIELD: self.loader_start})
+        if self.vmlinuz is not None:
+            metadata += dump({KERNEL_FILE_FIELD: self.vmlinuz})
+        if self.initrd is not None:
+            metadata += dump({INITRD_FILE_FIELD: self.initrd})
+        if self.dtb_file is not None:
+            # XXX In V3 this one should be a list, called dtb_files.
+            metadata += dump({DTB_FILE_FIELD: self.dtb_file})
+        if self.boot_script is not None:
+            metadata += dump({BOOT_SCRIPT_FIELD: self.boot_script})
+        if self.extra_serial_opts is not None:
+            # XXX Check why and where once we get a list once a string.
+            if isinstance(self.extra_serial_opts, list):
+                extra_serial_options = " ".join(self.extra_serial_opts)
+            else:
+                extra_serial_options = self.extra_serial_opts
+            metadata += dump({
+                            EXTRA_SERIAL_OPTIONS_FIELD:
+                            extra_serial_options})
+        if self.snowball_startup_files_config is not None:
+            metadata += dump({SNOWBALL_STARTUP_FILES_CONFIG_FIELD:
+                                self.snowball_startup_files_config})
+        if self.samsung_bl1_start is not None:
+            metadata += dump({SAMSUNG_BL1_START_FIELD: self.samsung_bl1_start})
+        if self.samsung_bl1_len is not None:
+            metadata += dump({SAMSUNG_BL1_LEN_FIELD: self.samsung_bl1_len})
+        if self.samsung_env_len is not None:
+            metadata += dump({SAMSUNG_ENV_LEN_FIELD: self.samsung_env_len})
+        if self.samsung_bl2_len is not None:
+            metadata += dump({SAMSUNG_BL2_LEN_FIELD: self.samsung_bl2_len})
+        return metadata
+
+    def create_metadata_old(self):
+        """Get the contents of the metadata file.
+
+        Creates a metadata file for v1 and v2 of the hwpack config file.
+        """
         metadata = "NAME=%s\n" % self.name
         metadata += "VERSION=%s\n" % self.version
         metadata += "ARCHITECTURE=%s\n" % self.architecture
@@ -264,7 +434,6 @@
             metadata += "SAMSUNG_ENV_LEN=%s\n" % self.samsung_env_len
         if self.samsung_bl2_len is not None:
             metadata += "SAMSUNG_BL2_LEN=%s\n" % self.samsung_bl2_len
-
         return metadata
 
 

=== modified file 'linaro_image_tools/hwpack/hardwarepack_format.py'
--- linaro_image_tools/hwpack/hardwarepack_format.py	2012-06-13 14:26:02 +0000
+++ linaro_image_tools/hwpack/hardwarepack_format.py	2012-07-10 14:15:04 +0000
@@ -57,3 +57,12 @@
         self.is_supported = True
         self.is_deprecated = False
         self.has_v2_fields = True
+
+
+class HardwarePackFormatV3(HardwarePackFormat):
+    def __init__(self):
+        super(HardwarePackFormatV3, self).__init__()
+        self.format_as_string = "3.0"
+        self.is_supported = True
+        self.is_deprecated = False
+        self.has_v2_fields = True

=== modified file 'linaro_image_tools/hwpack/hwpack_convert.py'
--- linaro_image_tools/hwpack/hwpack_convert.py	2012-07-19 15:10:43 +0000
+++ linaro_image_tools/hwpack/hwpack_convert.py	2012-07-20 08:19:11 +0000
@@ -46,6 +46,7 @@
     SPL_FILE_FIELD,
     WIRED_INTERFACES_FIELD,
     WIRELESS_INTERFACES_FIELD,
+    INCLUDE_DEBS_FIELD,
 )
 
 # This is the main section of an INI-style hwpack config file.
@@ -70,6 +71,7 @@
 
 # Old field, the only one with a dash: since the format is new, convert it.
 ASSUME_INSTALLED_OLD = 'assume-installed'
+INCLUDE_DEBS_OLD = 'include-debs'
 
 # The default bootloader for the bootloaders section.
 DEFAULT_BOOTLOADER = 'u_boot'
@@ -121,6 +123,8 @@
         self.wireless_interfaces = []
         # SPL entries
         self.spl = {}
+        # The list of packages that should be installed.
+        self.assume_installed = []
 
     def _parse(self):
         """Parses the config file and stores its values."""
@@ -171,9 +175,14 @@
                             elif key in UBOOT_KEYS:
                                 self._set_bootloaders(key, value)
                                 continue
-                            # Convert an old key into the new one.
+                            # Create list.
                             elif key == ASSUME_INSTALLED_OLD:
-                                key = ASSUME_INSTALLED_FIELD
+                                self.parse_list_string(
+                                                    self.assume_installed,
+                                                    value)
+                                continue
+                            elif key == INCLUDE_DEBS_OLD:
+                                key = INCLUDE_DEBS_FIELD
                             self.hwpack[key] = value
                 else:
                     # Here we have only sources sections.
@@ -231,6 +240,9 @@
         if self.architectures:
             archs = {ARCHITECTURES_FIELD: self.architectures}
             converted += dump(archs)
+        if self.assume_installed:
+            installed = {ASSUME_INSTALLED_FIELD: self.assume_installed}
+            converted += dump(installed)
         if self.extra_serial_options:
             serial_options = {EXTRA_SERIAL_OPTIONS_FIELD:
                                 self.extra_serial_options}
@@ -271,7 +283,7 @@
 
     :param python_object: The object to serialize.
     """
-    return yaml.dump(python_object, default_flow_style=False, indent=True)
+    return yaml.dump(python_object, default_flow_style=False)
 
 
 def check_and_validate_args(args):

=== modified file 'linaro_image_tools/hwpack/hwpack_fields.py'
--- linaro_image_tools/hwpack/hwpack_fields.py	2012-07-19 15:10:43 +0000
+++ linaro_image_tools/hwpack/hwpack_fields.py	2012-07-23 14:10:35 +0000
@@ -25,6 +25,7 @@
 # Try to keep it alphabetically sorted per section.
 #
 ARCHITECTURES_FIELD = 'architectures'
+ARCHITECTURE_FIELD = 'architecture'
 ASSUME_INSTALLED_FIELD = 'assume_installed'
 BOARDS_FIELD = 'boards'
 BOOTLOADERS_FIELD = 'bootloaders'
@@ -36,6 +37,7 @@
 DTB_FILES_FIELD = 'dtb_files'
 EXTRA_SERIAL_OPTIONS_FIELD = 'extra_serial_options'
 FORMAT_FIELD = 'format'
+INCLUDE_DEBS_FIELD = 'include_debs'
 INITRD_ADDR_FIELD = 'initrd_addr'
 INITRD_FILE_FIELD = 'initrd_file'
 KERNEL_ADDR_FIELD = 'kernel_addr'
@@ -55,6 +57,7 @@
 SUPPORT_FIELD = 'support'
 WIRED_INTERFACES_FIELD = 'wired_interfaces'
 WIRELESS_INTERFACES_FIELD = 'wireless_interfaces'
+VERSION_FIELD = 'version'
 
 # Bootloaders specific fields
 DD_FIELD = 'dd'

=== modified file 'linaro_image_tools/hwpack/tests/__init__.py'
--- linaro_image_tools/hwpack/tests/__init__.py	2012-07-19 07:49:47 +0000
+++ linaro_image_tools/hwpack/tests/__init__.py	2012-07-19 17:57:24 +0000
@@ -27,6 +27,7 @@
         'linaro_image_tools.hwpack.tests.test_better_tarfile',
         'linaro_image_tools.hwpack.tests.test_builder',
         'linaro_image_tools.hwpack.tests.test_config',
+        'linaro_image_tools.hwpack.tests.test_config_v3',
         'linaro_image_tools.hwpack.tests.test_hardwarepack',
         'linaro_image_tools.hwpack.tests.test_hwpack_converter',
         'linaro_image_tools.hwpack.tests.test_packages',

=== modified file 'linaro_image_tools/hwpack/tests/test_config.py'
--- linaro_image_tools/hwpack/tests/test_config.py	2012-06-13 14:32:49 +0000
+++ linaro_image_tools/hwpack/tests/test_config.py	2012-07-20 08:19:11 +0000
@@ -24,6 +24,9 @@
 from testtools import TestCase
 
 from linaro_image_tools.hwpack.config import Config, HwpackConfigError
+from linaro_image_tools.hwpack.hwpack_fields import (
+    DEFINED_PARTITION_LAYOUTS,
+)
 
 
 class ConfigTests(TestCase):
@@ -72,7 +75,7 @@
 
     def test_validate_no_name(self):
         config = self.get_config("[hwpack]\n")
-        self.assertValidationError("No name in the [hwpack] section", config)
+        self.assertValidationError("Empty value for name", config)
 
     def test_validate_empty_name(self):
         config = self.get_config("[hwpack]\nname =  \n")
@@ -87,7 +90,8 @@
                 "[hwpack]\nname = ahwpack\n"
                 "include-debs = if you don't mind\n")
         self.assertValidationError(
-            "Invalid value for include-debs: if you don't mind", config)
+            "Invalid value for include-debs: Not a boolean: if you don't mind",
+            config)
 
     def test_validate_invalid_supported(self):
         config = self.get_config(
@@ -192,11 +196,9 @@
         self.assertEqual(None, config.validate())
 
     def test_validate_supported_format(self):
-        config = self.get_config(
-                self.valid_start
-                + "\nformat = 0.9\n")
-        self.assertValidationError(
-            "Format version '0.9' is not supported.", config)
+        contents = self.valid_start + "format = 0.9\n"
+        config = Config(StringIO(contents))
+        self.assertRaises(HwpackConfigError, config.validate)
 
     def test_validate_invalid_u_boot_package_name(self):
         config = self.get_config(
@@ -307,7 +309,7 @@
             "Undefined partition layout %s in the [%s] section. "
             "Valid partition layouts are %s."
             % (partition_layout, 'hwpack',
-               ", ".join(config.DEFINED_PARTITION_LAYOUTS)), config)
+               ", ".join(DEFINED_PARTITION_LAYOUTS)), config)
 
     def test_validate_wired_interfaces(self):
         self.assertTrue("XXX What is an invalid interface name?")

=== added file 'linaro_image_tools/hwpack/tests/test_config_v3.py'
--- linaro_image_tools/hwpack/tests/test_config_v3.py	1970-01-01 00:00:00 +0000
+++ linaro_image_tools/hwpack/tests/test_config_v3.py	2012-07-20 13:47:37 +0000
@@ -0,0 +1,738 @@
+# Copyright (C) 2010 - 2012 Linaro
+#
+# Author: James Tunnicliffe <james.tunnicliffe@linaro.org>
+#
+# This file is part of Linaro Image Tools.
+#
+# Linaro Image Tools is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# Linaro Image Tools is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Linaro Image Tools; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+# USA.
+
+import re
+from StringIO import StringIO
+from testtools import TestCase
+
+from linaro_image_tools.hwpack.config import Config, HwpackConfigError
+from linaro_image_tools.hwpack.hwpack_fields import (
+    DEFINED_PARTITION_LAYOUTS,
+)
+
+
+class ConfigTests(TestCase):
+
+    valid_start = (
+        "name: ahwpack\npackages: foo\narchitectures: armel\n")
+    valid_start_v3 = valid_start + "format: 3.0\n"
+    valid_complete_v3 = (valid_start_v3 +
+        "serial_tty: ttySAC1\n"
+        "partition_layout:\n"
+        " - bootfs_rootfs\n"
+        "boot_script: boot.scr\n"
+        "extra_serial_options:\n"
+        "  - console=tty0\n"
+        "  - console=ttyO2,115200n8\n"
+        "mmc_id: 0:1\n"
+        "kernel_file: boot/vmlinuz-*-linaro-omap\n"
+        "initrd_file: boot/initrd.img-*-linaro-omap\n"
+        "dtb_file: boot/dt-*-linaro-omap/omap4-panda.dtb\n"
+        "bootloaders:\n"
+        " u_boot:\n"
+        "  package: u-boot-linaro-s5pv310\n"
+        "  file: usr/lib/u-boot/smdkv310/u-boot.bin\n"
+        "  spl_package: x-loader-omap4-panda\n"
+        "  spl_file: usr/lib/x-loader/omap4430panda/MLO\n"
+        "  in_boot_part: True\n"
+        "  extra_boot_options:\n"
+        "   - earlyprintk\n"
+        "   - fixrtc\n"
+        "   - nocompcache\n"
+        "   - vram=48M\n"
+        "   - omapfb.vram=0:24M\n"
+        "   - mem=456M@0x80000000\n"
+        "   - mem=512M@0xA0000000\n")
+    valid_end = "sources:\n    sources-entry: foo bar\n"
+
+    def test_create(self):
+        config = Config(StringIO())
+        self.assertTrue(config is not None)
+
+    def get_config(self, contents):
+        if not re.search("\s*format\s*:", contents):
+            contents = "format: 3.0\n" + contents
+        return Config(StringIO(contents), bootloader="u_boot")
+
+    def assertConfigError(self, contents, f, *args, **kwargs):
+        e = self.assertRaises(HwpackConfigError, f, *args, **kwargs)
+        self.assertEqual(contents, str(e))
+
+    def assertValidationError(self, contents, validate_function):
+        self.assertConfigError(contents, validate_function)
+
+    def test_validate_empty_name(self):
+        config = self.get_config("name:  ")
+        self.assertValidationError("Empty value for name",
+                                   config._validate_name)
+
+    def test_validate_invalid_name(self):
+        config = self.get_config("name: ~~\n")
+        self.assertValidationError("Invalid name: ~~",
+                                   config._validate_name)
+
+    def test_validate_invalid_include_debs(self):
+        config = self.get_config(
+                "name: ahwpack\n"
+                "include_debs: if you don't mind\n")
+        self.assertValidationError(
+            "Invalid value for include-debs: if you don't mind",
+            config._validate_include_debs)
+
+    def test_validate_invalid_supported(self):
+        config = self.get_config(
+                "name: ahwpack\nsupport: if you pay us\n")
+        self.assertValidationError(
+            "Invalid value for support: if you pay us",
+            config._validate_support)
+
+    def test_validate_no_packages(self):
+        config = self.get_config(
+                "name: ahwpack\n\n")
+        self.assertValidationError(
+            "No packages found in the metadata", config._validate_packages)
+
+    def test_validate_empty_packages(self):
+        config = self.get_config(
+                "name: ahwpack\npackages:  \n")
+        self.assertValidationError(
+            "No packages found in the metadata", config._validate_packages)
+
+    def test_validate_invalid_package_name(self):
+        config = self.get_config(
+                "name: ahwpack\npackages: foo  ~~ bar\n")
+        self.assertValidationError(
+            "Invalid value in packages in the metadata: ~~",
+            config._validate_packages)
+
+    def test_validate_no_architectures(self):
+        config = self.get_config(
+                "name: ahwpack\npackages: foo\n")
+        self.assertValidationError(
+            "No architectures found in the metadata",
+            config._validate_architectures)
+
+    def test_validate_empty_architectures(self):
+        config = self.get_config(
+                "name: ahwpack\npackages: foo\n"
+                "architectures: \n")
+        self.assertValidationError(
+            "No architectures found in the metadata",
+            config._validate_architectures)
+
+    def test_validate_invalid_package_name_in_assume_installed(self):
+        config = self.get_config(
+                "name: ahwpack\npackages: foo\n"
+                "architectures: armel\nassume_installed:\n - bar\n - ~~\n")
+        self.assertValidationError(
+            "Invalid value in assume-installed in the metadata: ~~",
+            config._validate_assume_installed)
+
+    def test_validate_other_section_empty_sources_entry(self):
+        config = self.get_config(
+                self.valid_start + "sources:\n ubuntu:  \n")
+        self.assertValidationError(
+            "The sources-entry, ubuntu is missing the URI",
+            config._validate_sources)
+
+    def test_validate_other_section_only_uri_in_sources_entry(self):
+        config = self.get_config(
+            self.valid_start + "sources:\n ubuntu: foo\n")
+        self.assertValidationError(
+            "The sources-entry, ubuntu is missing the distribution",
+            config._validate_sources)
+
+    def test_validate_other_section_sources_entry_starting_with_deb(self):
+        config = self.get_config(self.valid_start +
+                  "sources:\n ubuntu: deb http://example.org/ foo main\n")
+        self.assertValidationError(
+            "The sources-entry, ubuntu shouldn't start with 'deb'",
+            config._validate_sources)
+
+    def test_validate_other_section_sources_entry_starting_with_deb_src(self):
+        config = self.get_config(self.valid_start +
+            "sources:\n ubuntu: deb-src http://example.org/ foo main\n")
+        self.assertValidationError(
+            "The sources-entry, ubuntu shouldn't start with 'deb'",
+            config._validate_sources)
+
+    def test_validate_valid_config(self):
+        config = self.get_config(self.valid_complete_v3)
+        self.assertEqual(None, config.validate())
+
+    def test_validate_supported_format(self):
+        config = self.get_config(self.valid_start + "format: 0.9\n")
+        self.assertValidationError(
+            "Format version '0.9' is not supported.", config._validate_format)
+
+    def test_validate_invalid_u_boot_package_name(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "bootloaders:\n"
+                                 " u_boot:\n"
+                                 "  package: ~~\n")
+        self.assertValidationError(
+            "Invalid value in u_boot_package in the metadata: ~~",
+            config._validate_u_boot_package)
+
+    def test_validate_invalid_u_boot_file(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "bootloaders:\n"
+                                 " u_boot:\n"
+                                 "  file: ~~\n")
+        self.assertValidationError("Invalid path: ~~",
+                                   config._validate_u_boot_file)
+
+    def test_validate_invalid_kernel_file(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "kernel_file: ~~\n")
+        self.assertValidationError("Invalid path: ~~",
+                                   config._validate_vmlinuz)
+
+    def test_validate_empty_kernel_file(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "kernel_file:  \n")
+        self.assertValidationError("No kernel_file found in the metadata",
+                                   config._validate_vmlinuz)
+
+    def test_validate_invalid_initrd_file(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "initrd_file: ~~\n")
+        self.assertValidationError("Invalid path: ~~", config._validate_initrd)
+
+    def test_validate_empty_initrd_file(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "kernel_file:  \n")
+        self.assertValidationError("No initrd_file found in the metadata",
+                                   config._validate_initrd)
+
+    def test_validate_invalid_boot_script(self):
+        config = self.get_config(self.valid_start_v3 + "boot_script: ~~")
+        self.assertValidationError("Invalid path: ~~",
+                                   config._validate_boot_script)
+
+    def test_validate_invalid_dtb_file(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "dtb_file: ~~\n")
+        self.assertValidationError("Invalid path: ~~",
+                                   config._validate_dtb_file)
+
+    def test_validate_invalid_spl_package_name(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "bootloaders:\n"
+                                 " u_boot:\n"
+                                 "  spl_package: ~~\n")
+        config.set_board("panda")
+        self.assertValidationError(
+            "Invalid value in spl_package in the metadata: ~~",
+            config._validate_spl_package)
+
+    def test_validate_invalid_spl_file(self):
+        config = self.get_config(self.valid_start_v3 +
+                                 "boards:\n"
+                                 " panda:\n"
+                                 "  bootloaders:\n"
+                                 "   u_boot:\n"
+                                 "    spl_file: ~~\n")
+        config.set_board("panda")
+        self.assertValidationError("Invalid path: ~~",
+                                   config._validate_spl_file)
+
+    def test_validate_partition_layout(self):
+        partition_layout = 'apafs_bananfs'
+        config = self.get_config(self.valid_start_v3 +
+                                 "partition_layout: " + partition_layout)
+        self.assertValidationError(
+            "Undefined partition layout %s. "
+            "Valid partition layouts are %s."
+            % (partition_layout,
+               ", ".join(DEFINED_PARTITION_LAYOUTS)),
+            config._validate_partition_layout)
+
+    def test_validate_wired_interfaces(self):
+        self.assertTrue("XXX What is an invalid interface name?")
+
+    def test_validate_wireless_interfaces(self):
+        self.assertTrue("XXX What is an invalid interface name?")
+
+    def test_validate_u_boot_in_boot_part_bool(self):
+        config = self.get_config(
+            self.valid_start_v3 +
+            "bootloaders:\n"
+            "   u_boot:\n"
+            "    in_boot_part: Nope\n")
+        self.assertValidationError(
+            "Invalid value for u_boot_in_boot_part: Nope",
+            config._validate_uboot_in_boot_part)
+
+    def test_find_board_specific_variable(self):
+        config = self.get_config(
+            self.valid_start_v3 +
+            "boards:\n"
+            " panda:\n"
+            "  bootloaders:\n"
+            "   u_boot:\n"
+            "    in_boot_part: Yes\n")
+
+        config.set_bootloader("u_boot")
+        config.set_board("panda")
+
+        config._validate_uboot_in_boot_part()
+        self.assertEqual(config.uboot_in_boot_part, "yes")
+
+    def test_board_specific_overwrites_global(self):
+        config = self.get_config(
+            self.valid_start_v3 +
+            "bootloaders:\n"
+            " u_boot:\n"
+            "  in_boot_part: No\n"
+            "boards:\n"
+            " panda:\n"
+            "  bootloaders:\n"
+            "   u_boot:\n"
+            "    in_boot_part: Yes\n")
+
+        config.set_bootloader("u_boot")
+        config.set_board("panda")
+
+        config._validate_uboot_in_boot_part()
+        self.assertEqual(config.uboot_in_boot_part, "yes")
+
+    def test_validate_serial_tty(self):
+        config = self.get_config(self.valid_start_v3 + "serial_tty: tty\n")
+        self.assertValidationError("Invalid serial tty: tty",
+                                   config._validate_serial_tty)
+
+        config = self.get_config(self.valid_start_v3 + "serial_tty: ttxSAC1\n")
+        self.assertValidationError("Invalid serial tty: ttxSAC1",
+                                   config._validate_serial_tty)
+
+    def test_validate_mmc_id(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "mmc_id: x\n")
+        self.assertValidationError("Invalid mmc_id x", config._validate_mmc_id)
+
+    def test_validate_boot_min_size(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "boot_min_size: x\n")
+        self.assertValidationError("Invalid boot min size x",
+                                   config._validate_boot_min_size)
+
+    def test_validate_root_min_size(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "root_min_size: x\n")
+        self.assertValidationError("Invalid root min size x",
+                                   config._validate_root_min_size)
+
+    def test_validate_loader_min_size(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "loader_min_size: x\n")
+        self.assertValidationError("Invalid loader min size x",
+                                   config._validate_loader_min_size)
+
+    def test_validate_kernel_addr(self):
+        # V3 change: All numerical inputs are good addresses (since YAML
+        # converts them to ingegers and we convert them back to the correct
+        # format). We don't need 8 digit hex values for addresses.
+        config = self.get_config(self.valid_complete_v3 +
+                                 "kernel_addr: 0x8000000\n")
+        config._validate_kernel_addr()
+        config = self.get_config(self.valid_complete_v3 +
+                                 "kernel_addr: 0x8000000x\n")
+        self.assertValidationError(
+            "Invalid kernel address: 0x8000000x", config._validate_kernel_addr)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "kernel_addr: 80000000\n")
+        config._validate_kernel_addr()
+
+    def test_validate_initrd_addr(self):
+        # V3 change: All numerical inputs are good addresses (since YAML
+        # converts them to ingegers and we convert them back to the correct
+        # format). We don't need 8 digit hex values for addresses.
+        config = self.get_config(self.valid_complete_v3 +
+                                 "initrd_addr: 0x8000000\n")
+        config._validate_initrd_addr()
+        config = self.get_config(self.valid_complete_v3 +
+                                 "initrd_addr: 0x8000000x\n")
+        self.assertValidationError(
+            "Invalid initrd address: 0x8000000x", config._validate_initrd_addr)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "initrd_addr: 80000000\n")
+        config._validate_initrd_addr()
+
+    def test_validate_load_addr(self):
+        # V3 change: All numerical inputs are good addresses (since YAML
+        # converts them to ingegers and we convert them back to the correct
+        # format). We don't need 8 digit hex values for addresses.
+        config = self.get_config(self.valid_complete_v3 +
+                                 "load_addr: 0x8000000\n")
+        config._validate_load_addr()
+        config = self.get_config(self.valid_complete_v3 +
+                                 "load_addr: 0x8000000x\n")
+        self.assertValidationError("Invalid load address: 0x8000000x",
+                                   config._validate_load_addr)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "load_addr: 80000000\n")
+        config._validate_load_addr()
+
+    def test_validate_dtb_addr(self):
+        # V3 change: All numerical inputs are good addresses (since YAML
+        # converts them to ingegers and we convert them back to the correct
+        # format). We don't need 8 digit hex values for addresses.
+        config = self.get_config(self.valid_complete_v3 +
+                                 "dtb_addr: 0x8000000\n")
+        config._validate_dtb_addr()
+        config = self.get_config(self.valid_complete_v3 +
+                                 "dtb_addr: 0x8000000x\n")
+        self.assertValidationError("Invalid dtb address: 0x8000000x",
+                                   config._validate_dtb_addr)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "dtb_addr: 80000000\n")
+        config._validate_dtb_addr()
+
+    def test_wired_interfaces(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "wired_interfaces:\n - eth0\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual(["eth0"], config.wired_interfaces)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "wired_interfaces:\n"
+                                 " - eth0\n"
+                                 " - eth1\n"
+                                 " - usb2\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual(["eth0", "eth1", "usb2"], config.wired_interfaces)
+
+    def test_wireless_interfaces(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "wireless_interfaces:\n"
+                                 " - wlan0\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual(["wlan0"], config.wireless_interfaces)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "wireless_interfaces:\n"
+                                 " - wlan0\n"
+                                 " - wl1\n"
+                                 " - usb2\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual(["wlan0", "wl1", "usb2"], config.wireless_interfaces)
+
+    def test_partition_layout(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("bootfs_rootfs",
+                         config.partition_layout)
+
+    def test_u_boot_file(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("usr/lib/u-boot/smdkv310/u-boot.bin",
+                         config.u_boot_file)
+
+    def test_u_boot_package(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("u-boot-linaro-s5pv310",
+                         config.u_boot_package)
+
+    def test_spl_file(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("usr/lib/x-loader/omap4430panda/MLO",
+                         config.spl_file)
+
+    def test_kernel_file(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("boot/vmlinuz-*-linaro-omap",
+                         config.vmlinuz)
+
+    def test_initrd_file(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("boot/initrd.img-*-linaro-omap",
+                         config.initrd)
+
+    def test_dtb_file(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("boot/dt-*-linaro-omap/omap4-panda.dtb",
+                         config.dtb_file)
+
+    def test_extra_boot_options(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual(
+            "earlyprintk fixrtc nocompcache vram=48M "
+            "omapfb.vram=0:24M mem=456M@0x80000000 mem=512M@0xA0000000",
+            config.extra_boot_options)
+
+    def test_extra_serial_opts(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual('console=tty0 console=ttyO2,115200n8',
+                         config.extra_serial_opts)
+
+    def test_boot_script(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("boot.scr",
+                         config.boot_script)
+
+    def test_u_boot_in_boot_part(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("yes",
+                         config.uboot_in_boot_part)
+
+    def test_spl_package(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("x-loader-omap4-panda",
+                         config.spl_package)
+
+    def test_serial_tty(self):
+        config = self.get_config(self.valid_complete_v3 + self.valid_end)
+        config.validate()
+        self.assertEqual("ttySAC1", config.serial_tty)
+
+    def test_mmc_id(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "mmc_id: 0:1\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0:1", config.mmc_id)
+
+    def test_boot_min_size(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "boot_min_size: 50\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("50", config.boot_min_size)
+
+    def test_root_min_size(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "root_min_size: 50\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("50", config.root_min_size)
+
+    def test_loader_min_size(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "loader_min_size: 2\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("2", config.loader_min_size)
+
+    def test_kernel_addr(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "kernel_addr: 0x80000000\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0x80000000", config.kernel_addr)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "kernel_addr: 0x8aBcdEFf\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0x8abcdeff", config.kernel_addr)
+
+    def test_initrd_addr(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "initrd_addr: 0x80000000\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0x80000000", config.initrd_addr)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "initrd_addr: 0x8aBcdEFf\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0x8abcdeff", config.initrd_addr)
+
+    def test_load_addr(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "load_addr: 0x80000000\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0x80000000", config.load_addr)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "load_addr: 0x8aBcdEFf\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0x8abcdeff", config.load_addr)
+
+    def test_dtb_addr(self):
+        config = self.get_config(self.valid_complete_v3 +
+                                 "dtb_addr: 0x80000000\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0x80000000", config.dtb_addr)
+        config = self.get_config(self.valid_complete_v3 +
+                                 "dtb_addr: 0x8aBcdEFf\n" +
+                                 self.valid_end)
+        config.validate()
+        self.assertEqual("0x8abcdeff", config.dtb_addr)
+
+    def test_name(self):
+        config = self.get_config(
+            "name: ahwpack\n"
+            "packages: foo\n"
+            "architectures: armel\n")
+        self.assertEqual("ahwpack", config.name)
+
+    def test_include_debs(self):
+        config = self.get_config(self.valid_start + "include_debs: false\n")
+        self.assertEqual(False, config.include_debs)
+
+    def test_include_debs_defaults_true(self):
+        config = self.get_config(self.valid_start)
+        self.assertEqual(True, config.include_debs)
+
+    def test_include_debs_defaults_true_on_empty(self):
+        config = self.get_config(self.valid_start + "include_debs: \n")
+        self.assertEqual(True, config.include_debs)
+
+    def test_origin(self):
+        config = self.get_config(self.valid_start + "origin: linaro\n")
+        self.assertEqual("linaro", config.origin)
+
+    def test_origin_default_None(self):
+        config = self.get_config(self.valid_start)
+        self.assertEqual(None, config.origin)
+
+    def test_origin_None_on_empty(self):
+        config = self.get_config(self.valid_start + "origin:  \n")
+        self.assertEqual(None, config.origin)
+
+    def test_maintainer(self):
+        maintainer = "Linaro Developers <linaro-dev@lists.linaro.org>"
+        config = self.get_config(
+            self.valid_start
+            + "maintainer: %s\n" % maintainer)
+        self.assertEqual(maintainer, config.maintainer)
+
+    def test_maintainer_default_None(self):
+        config = self.get_config(self.valid_start)
+        self.assertEqual(None, config.maintainer)
+
+    def test_maintainer_None_on_empty(self):
+        config = self.get_config(self.valid_start + "maintainer:  \n")
+        self.assertEqual(None, config.maintainer)
+
+    def test_support_supported(self):
+        config = self.get_config(self.valid_start + "support: supported\n")
+        self.assertEqual("supported", config.support)
+
+    def test_support_unsupported(self):
+        config = self.get_config(self.valid_start + "support: unsupported\n")
+        self.assertEqual("unsupported", config.support)
+
+    def test_support_default_None(self):
+        config = self.get_config(self.valid_start)
+        self.assertEqual(None, config.support)
+
+    def test_support_None_on_empty(self):
+        config = self.get_config(self.valid_start + "support:  \n")
+        self.assertEqual(None, config.support)
+
+    def test_packages(self):
+        config = self.get_config(
+            "name: ahwpack\n"
+            "packages:\n"
+            " - foo\n"
+            " - bar\n"
+            "architectures: armel\n")
+        self.assertEqual(["foo", "bar"], config.packages)
+
+    def test_packages_filters_duplicates(self):
+        config = self.get_config(
+            "name: ahwpack\n"
+            "packages:\n"
+            " - foo\n"
+            " - bar\n"
+            " - foo\n"
+            "architectures: armel\n")
+        self.assertEqual(["foo", "bar"], config.packages)
+
+    def test_sources_single(self):
+        config = self.get_config(
+            self.valid_start
+            + "sources:\n"
+              " ubuntu: http://example.org foo\n")
+        self.assertEqual({"ubuntu": "http://example.org foo"}, config.sources)
+
+    def test_sources_multiple(self):
+        config = self.get_config(
+            self.valid_start
+            + "sources:\n"
+              " ubuntu: http://example.org foo\n"
+              " linaro: http://example.org bar\n")
+        self.assertEqual(
+            {"ubuntu": "http://example.org foo",
+             "linaro": "http://example.org bar"},
+            config.sources)
+
+    def test_architectures(self):
+        config = self.get_config(
+            "hello: there\n"
+            "name: ahwpack\n"
+            "packages: foo\n"
+            "architectures:\n"
+            " - foo\n"
+            " - bar\n")
+        self.assertEqual(["foo", "bar"], config.architectures)
+
+    def test_architectures_filters_duplicates(self):
+        config = self.get_config(
+            "name: ahwpack\n"
+            "packages: foo\n"
+            "architectures:\n"
+            " - foo\n"
+            " - bar\n"
+            " - foo\n")
+        self.assertEqual(["foo", "bar"], config.architectures)
+
+    def test_assume_installed(self):
+        config = self.get_config(
+            "name: ahwpack\n"
+            "packages:\n"
+            " - foo\n"
+            "architectures:\n"
+            " - armel\n"
+            "assume_installed:\n"
+            " - foo\n"
+            " - bar\n")
+        self.assertEqual(["foo", "bar"], config.assume_installed)
+
+    def test_assume_installed_filters_duplicates(self):
+        config = self.get_config(
+            "name: ahwpack\n"
+            "packages:\n"
+            " - foo\n"
+            "architectures:\n"
+            " - armel\n"
+            "assume_installed:\n"
+            " - foo\n"
+            " - bar\n"
+            " - foo\n")
+        self.assertEqual(["foo", "bar"], config.assume_installed)

=== modified file 'linaro_image_tools/hwpack/tests/test_hardwarepack.py'
--- linaro_image_tools/hwpack/tests/test_hardwarepack.py	2012-06-13 14:53:32 +0000
+++ linaro_image_tools/hwpack/tests/test_hardwarepack.py	2012-07-19 14:50:45 +0000
@@ -40,6 +40,7 @@
 from linaro_image_tools.hwpack.hardwarepack_format import (
     HardwarePackFormatV1,
     HardwarePackFormatV2,
+    HardwarePackFormatV3,
     )
 
 
@@ -87,174 +88,157 @@
 
     def test_str(self):
         metadata = Metadata("ahwpack", "4", "armel")
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n", str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_origin(self):
         metadata = Metadata("ahwpack", "4", "armel", origin="linaro")
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\nORIGIN=linaro\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "ORIGIN=linaro\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_maintainer(self):
         metadata = Metadata(
             "ahwpack", "4", "armel", maintainer="Some Maintainer")
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "MAINTAINER=Some Maintainer\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "MAINTAINER=Some Maintainer\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_support(self):
         metadata = Metadata("ahwpack", "4", "armel", support="unsupported")
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "SUPPORT=unsupported\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "SUPPORT=unsupported\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_serial_tty(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(serial_tty='ttyO2')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "SERIAL_TTY=ttyO2\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "SERIAL_TTY=ttyO2\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_kernel_addr(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(kernel_addr='0x80000000')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "KERNEL_ADDR=0x80000000\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "KERNEL_ADDR=0x80000000\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_initrd_addr(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(initrd_addr='0x80000000')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "INITRD_ADDR=0x80000000\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "INITRD_ADDR=0x80000000\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_load_addr(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(load_addr='0x80000000')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "LOAD_ADDR=0x80000000\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "LOAD_ADDR=0x80000000\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_dtb_addr(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(dtb_addr='0x80000000')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "DTB_ADDR=0x80000000\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "DTB_ADDR=0x80000000\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_wired_interfaces(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(wired_interfaces=['eth0', 'usb0'])
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "WIRED_INTERFACES=eth0 usb0\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "WIRED_INTERFACES=eth0 usb0\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_wireless_interfaces(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(wireless_interfaces=['wlan0', 'wl0'])
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "WIRELESS_INTERFACES=wlan0 wl0\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "WIRELESS_INTERFACES=wlan0 wl0\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_partition_layout(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(partition_layout='bootfs_rootfs')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "PARTITION_LAYOUT=bootfs_rootfs\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "PARTITION_LAYOUT=bootfs_rootfs\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_mmc_id(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(mmc_id='1')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "MMC_ID=1\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "MMC_ID=1\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_boot_min_size(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(boot_min_size='50')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "BOOT_MIN_SIZE=50\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "BOOT_MIN_SIZE=50\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_root_min_size(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(root_min_size='100')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "ROOT_MIN_SIZE=100\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "ROOT_MIN_SIZE=100\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_loader_min_size(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(loader_min_size='1')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "LOADER_MIN_SIZE=1\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "LOADER_MIN_SIZE=1\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_kernel_file(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(vmlinuz='boot/vmlinuz-3.0.0-1002-linaro-omap')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "KERNEL_FILE=boot/vmlinuz-3.0.0-1002-linaro-omap\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "KERNEL_FILE=boot/vmlinuz-3.0.0-1002-linaro-omap\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_initrd_file(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(initrd='boot/initrd.img-3.0.0-1002-linaro-omap')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "INITRD_FILE=boot/initrd.img-3.0.0-1002-linaro-omap\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "INITRD_FILE=boot/initrd.img-3.0.0-1002-linaro-omap\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_dtb_file(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(
             dtb_file='boot/dt-3.0.0-1002-linaro-omap/omap4-panda.dtb')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "DTB_FILE=boot/dt-3.0.0-1002-linaro-omap/omap4-panda.dtb\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\n"
+                        "ARCHITECTURE=armel\nDTB_FILE="
+                        "boot/dt-3.0.0-1002-linaro-omap/omap4-panda.dtb\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_boot_script(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(boot_script='boot.scr')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "BOOT_SCRIPT=boot.scr\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "BOOT_SCRIPT=boot.scr\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_extra_boot_options(self):
         metadata = Metadata("ahwpack", "4", "armel",
@@ -263,21 +247,21 @@
             extra_boot_options=(
                 'earlyprintk fixrtc nocompcache vram=48M omapfb.vram=0:24M '
                 'mem=456M@0x80000000 mem=512M@0xA0000000'))
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "EXTRA_BOOT_OPTIONS=earlyprintk fixrtc nocompcache vram=48M "
-            "omapfb.vram=0:24M mem=456M@0x80000000 mem=512M@0xA0000000\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
+                        "EXTRA_BOOT_OPTIONS=earlyprintk fixrtc nocompcache "
+                        "vram=48M omapfb.vram=0:24M "
+                        "mem=456M@0x80000000 mem=512M@0xA0000000\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_str_with_extra_serial_options(self):
         metadata = Metadata("ahwpack", "4", "armel",
                             format=HardwarePackFormatV2())
         metadata.add_v2_config(
             extra_serial_opts='console=tty0 console=ttyO2,115200n8')
-        self.assertEqual(
-            "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\n"
-            "EXTRA_SERIAL_OPTIONS=console=tty0 console=ttyO2,115200n8\n",
-            str(metadata))
+        expected_out = ("NAME=ahwpack\nVERSION=4\n"
+                        "ARCHITECTURE=armel\nEXTRA_SERIAL_OPTIONS="
+                        "console=tty0 console=ttyO2,115200n8\n")
+        self.assertEqual(expected_out, str(metadata))
 
     def test_from_config(self):
         class Config:
@@ -296,6 +280,113 @@
         self.assertEqual("i386", metadata.architecture)
 
 
+class NewMetadataTests(TestCase):
+
+    def setUp(self):
+        super(NewMetadataTests, self).setUp()
+
+    def test_format(self):
+        metadata = Metadata("ahwpack", "4", "armel",
+                                    format=HardwarePackFormatV3())
+        # Need to call also this one!
+        metadata.add_v2_config()
+        metadata.add_v3_config(bootloaders=None)
+        expected_out = ("format: '3.0'\nname: ahwpack\nversion: '4'\n"
+                        "architecture: armel\n")
+        self.assertEqual(expected_out, str(metadata))
+
+    def test_section_bootloaders(self):
+        bootloaders = {'u_boot': {'file': 'a_file'}}
+        metadata = Metadata("ahwpack", "4", "armel",
+                                    format=HardwarePackFormatV3())
+        # Need to call also this one!
+        metadata.add_v2_config()
+        metadata.add_v3_config(bootloaders=bootloaders)
+        expected_out = ("format: '3.0'\nname: ahwpack\nversion: '4'\n"
+                        "architecture: armel\nbootloaders:\n  u_boot:\n"
+                        "    file: a_file\n")
+        self.assertEqual(expected_out, str(metadata))
+
+    def test_section_wireless(self):
+        metadata = Metadata("ahwpack", "4", "armel",
+                                    format=HardwarePackFormatV3())
+        wireless_list = ['wlan0', 'wl0']
+        # Need to call also this one!
+        metadata.add_v2_config(wireless_interfaces=wireless_list)
+        metadata.add_v3_config(bootloaders=None)
+        expected_out = ("format: '3.0'\nname: ahwpack\nversion: '4'\n"
+                        "architecture: armel\nwireless_interfaces: wlan0 "
+                        "wl0\n")
+        self.assertEqual(expected_out, str(metadata))
+
+    def test_section_wired(self):
+        metadata = Metadata("ahwpack", "4", "armel",
+                                    format=HardwarePackFormatV3())
+        wired_list = ['eth0', 'usb0']
+        # Need to call also this one!
+        metadata.add_v2_config(wired_interfaces=wired_list)
+        metadata.add_v3_config(bootloaders=None)
+        expected_out = ("format: '3.0'\nname: ahwpack\nversion: '4'\n"
+                        "architecture: armel\nwired_interfaces: eth0 usb0\n")
+        self.assertEqual(expected_out, str(metadata))
+
+    def test_section_extra_serial_options(self):
+        metadata = Metadata("ahwpack", "4", "armel",
+                                    format=HardwarePackFormatV3())
+        options = ['option1', 'option2,option3']
+        # Need to call also this one!
+        metadata.add_v2_config(extra_serial_opts=options)
+        metadata.add_v3_config(bootloaders=None)
+        expected_out = ("format: '3.0'\nname: ahwpack\nversion: '4'\n"
+                        "architecture: armel\nextra_serial_options: option1 "
+                        "option2,option3\n")
+        self.assertEqual(expected_out, str(metadata))
+
+    def test_loop_through_for_spl(self):
+        bootloaders = {'u_boot': {'file': 'a_file', 'spl_file': 'some_value'}}
+        spl = 'spl-path'
+        metadata = Metadata("ahwpack", "4", "armel",
+                                    format=HardwarePackFormatV3())
+        # Need to call also this one!
+        metadata.add_v2_config()
+        metadata.add_v3_config(bootloaders=bootloaders)
+        metadata.spl = spl
+        expected_out = ("format: '3.0'\nname: ahwpack\nversion: '4'\n"
+                        "architecture: armel\nbootloaders:\n  u_boot:\n"
+                        "    file: a_file\n    spl_file: spl-path\n")
+        self.assertEqual(expected_out, str(metadata))
+
+    def test_loop_through_for_uboot(self):
+        bootloaders = {'u_boot': {'file': 'a_file', 'spl_file': 'some_value'}}
+        u_boot = 'uboot-path'
+        metadata = Metadata("ahwpack", "4", "armel",
+                                    format=HardwarePackFormatV3())
+        # Need to call also this one!
+        metadata.add_v2_config()
+        metadata.add_v3_config(bootloaders=bootloaders)
+        metadata.u_boot = u_boot
+        expected_out = ("format: '3.0'\nname: ahwpack\nversion: '4'\n"
+                        "architecture: armel\nbootloaders:\n  u_boot:\n"
+                        "    file: uboot-path\n    spl_file: some_value\n")
+        self.assertEqual(expected_out, str(metadata))
+
+    def test_loop_through_multiple_bootloaders(self):
+        bootloaders = {'u_boot': {'file': 'a_file', 'spl_file': 'some_value'},
+                        'uefi': {'spl_file': 'some_other_value'}}
+        spl = 'spl-path'
+        metadata = Metadata("ahwpack", "4", "armel",
+                                    format=HardwarePackFormatV3())
+        # Need to call also this one!
+        metadata.add_v2_config()
+        metadata.add_v3_config(bootloaders=bootloaders)
+        metadata.spl = spl
+        expected_out = ("format: '3.0'\nname: ahwpack\nversion: '4'\n"
+                        "architecture: armel\nbootloaders:\n  u_boot:\n"
+                        "    file: a_file\n    spl_file: spl-path\n  uefi:\n"
+                        "    spl_file: spl-path\n")
+        self.assertEqual(expected_out, str(metadata))
+
+
 class HardwarePackTests(TestCase):
 
     def setUp(self):

=== modified file 'linaro_image_tools/hwpack/tests/test_hwpack_converter.py'
--- linaro_image_tools/hwpack/tests/test_hwpack_converter.py	2012-07-19 15:21:06 +0000
+++ linaro_image_tools/hwpack/tests/test_hwpack_converter.py	2012-07-20 08:19:11 +0000
@@ -130,3 +130,27 @@
         converter = HwpackConverter(input_file, output_file)
         converter._parse()
         self.assertEqual(out_format, str(converter))
+
+    def test_assume_installed(self):
+        """Tests the correct creation of the extra_serial_options part."""
+        ini_format = ("[hwpack]\nformat=2.0\nassume-installed=install1 "
+                        "install2")
+        out_format = ("format: '3.0'\nassume_installed:\n- install1\n- "
+                        "install2\n")
+        input_file = self.useFixture(CreateTempFileFixture(ini_format)).\
+                                                                get_file_name()
+        output_file = self.useFixture(CreateTempFileFixture()).get_file_name()
+        converter = HwpackConverter(input_file, output_file)
+        converter._parse()
+        self.assertEqual(out_format, str(converter))
+
+    def test_include_debs(self):
+        """Tests the correct creation of the extra_serial_options part."""
+        ini_format = ("[hwpack]\nformat=2.0\ninclude-debs=yes")
+        out_format = ("format: '3.0'\ninclude_debs: true\n")
+        input_file = self.useFixture(CreateTempFileFixture(ini_format)).\
+                                                                get_file_name()
+        output_file = self.useFixture(CreateTempFileFixture()).get_file_name()
+        converter = HwpackConverter(input_file, output_file)
+        converter._parse()
+        self.assertEqual(out_format, str(converter))

=== modified file 'linaro_image_tools/media_create/boards.py'
--- linaro_image_tools/media_create/boards.py	2012-06-13 14:41:42 +0000
+++ linaro_image_tools/media_create/boards.py	2012-07-23 15:28:30 +0000
@@ -36,6 +36,7 @@
 import shutil
 import string
 import logging
+from linaro_image_tools.hwpack.config import Config
 
 from parted import Device
 
@@ -43,7 +44,7 @@
 
 from linaro_image_tools.media_create.partitions import (
     partition_mounted, SECTOR_SIZE, register_loopback)
-
+from StringIO import StringIO
 
 KERNEL_GLOB = 'vmlinuz-*-%(kernel_flavor)s'
 INITRD_GLOB = 'initrd.img-*-%(kernel_flavor)s'
@@ -114,6 +115,7 @@
 class HardwarepackHandler(object):
     FORMAT_1 = '1.0'
     FORMAT_2 = '2.0'
+    FORMAT_3 = '3.0'
     FORMAT_MIXED = '1.0and2.0'
     metadata_filename = 'metadata'
     format_filename = 'FORMAT'
@@ -158,18 +160,18 @@
         if self.tempdir is not None and os.path.exists(self.tempdir):
             shutil.rmtree(self.tempdir)
 
-    def get_field(self, section, field):
+    def get_field(self, field):
         data = None
         hwpack_with_data = None
         for hwpack_tarfile in self.hwpack_tarfiles:
             metadata = hwpack_tarfile.extractfile(self.metadata_filename)
-            # Use RawConfigParser which does not support the magical
-            # interpolation behavior of ConfigParser so we don't mess up
-            # metadata accidentally.
-            parser = ConfigParser.RawConfigParser()
-            parser.readfp(self.FakeSecHead(metadata))
+            lines = metadata.readlines()
+            if re.search("=", lines[0]) and not re.search(":", lines[0]):
+                # Probably V2 hardware pack without [hwpack] on the first line
+                lines = ["[hwpack]\n"] + lines
+            parser = Config(StringIO("".join(lines)))
             try:
-                new_data = parser.get(section, field)
+                new_data = parser.get_option(field)
                 if new_data is not None:
                     assert data is None, "The metadata field '%s' is set to " \
                         "'%s' and new value '%s' is found" % (field, data,
@@ -178,11 +180,12 @@
                     hwpack_with_data = hwpack_tarfile
             except ConfigParser.NoOptionError:
                 continue
+
         return data, hwpack_with_data
 
     def get_format(self):
         format = None
-        supported_formats = [self.FORMAT_1, self.FORMAT_2]
+        supported_formats = [self.FORMAT_1, self.FORMAT_2, self.FORMAT_3]
         for hwpack_tarfile in self.hwpack_tarfiles:
             format_file = hwpack_tarfile.extractfile(self.format_filename)
             format_string = format_file.read().strip()
@@ -196,8 +199,7 @@
         return format
 
     def get_file(self, file_alias):
-        file_name, hwpack_tarfile = self.get_field(self.main_section,
-                                                   file_alias)
+        file_name, hwpack_tarfile = self.get_field(file_alias)
         if file_name is not None:
             hwpack_tarfile.extract(file_name, self.tempdir)
             file_name = os.path.join(self.tempdir, file_name)
@@ -289,8 +291,7 @@
     def get_metadata_field(cls, field_name):
         """ Return the metadata value for field_name if it can be found.
         """
-        data, _ = cls.hardwarepack_handler.get_field(
-            cls.hardwarepack_handler.main_section, field_name)
+        data, _ = cls.hardwarepack_handler.get_field(field_name)
         return data
 
     @classmethod
@@ -305,7 +306,7 @@
             if (cls.hwpack_format == cls.hardwarepack_handler.FORMAT_1):
                 return
 
-            if (cls.hwpack_format == cls.hardwarepack_handler.FORMAT_2):
+            if (cls.hwpack_format != cls.hardwarepack_handler.FORMAT_1):
                 # Clear V1 defaults.
                 cls.kernel_addr = None
                 cls.initrd_addr = None
@@ -334,19 +335,19 @@
             cls.serial_tty = cls.get_metadata_field('serial_tty')
             wired_interfaces = cls.get_metadata_field('wired_interfaces')
             if wired_interfaces is not None:
-                cls.wired_interfaces = wired_interfaces.split(' ')
+                cls.wired_interfaces = wired_interfaces
             wireless_interfaces = cls.get_metadata_field(
                 'wireless_interfaces')
             if wireless_interfaces is not None:
-                cls.wireless_interfaces = wireless_interfaces.split(' ')
-            cls.vmlinuz = cls.get_metadata_field('kernel_file')
-            cls.initrd = cls.get_metadata_field('initrd_file')
+                cls.wireless_interfaces = wireless_interfaces
+            cls.vmlinuz = cls.get_metadata_field('vmlinuz')
+            cls.initrd = cls.get_metadata_field('initrd')
             cls.dtb_file = cls.get_metadata_field('dtb_file')
             cls.extra_boot_args_options = cls.get_metadata_field(
                 'extra_boot_options')
             cls.boot_script = cls.get_metadata_field('boot_script')
             cls.extra_serial_opts = cls.get_metadata_field(
-                'extra_serial_options')
+                'extra_serial_opts')
             cls.snowball_startup_files_config = cls.get_metadata_field(
                 'snowball_startup_files_config')
 
@@ -379,7 +380,7 @@
                     align_up(int(loader_min_size) * 1024 ** 2,
                              SECTOR_SIZE) / SECTOR_SIZE)
 
-            uboot_in_boot_part = cls.get_metadata_field('u_boot_in_boot_part')
+            uboot_in_boot_part = cls.get_metadata_field('uboot_in_boot_part')
             if uboot_in_boot_part is None:
                 cls.uboot_in_boot_part = False
             elif string.lower(uboot_in_boot_part) == 'yes':
@@ -401,7 +402,7 @@
             elif string.lower(env_dd) == 'no':
                 cls.env_dd = False
 
-            uboot_dd = cls.get_metadata_field('u_boot_dd')
+            uboot_dd = cls.get_metadata_field('uboot_dd')
             # Either uboot_dd is not specified, or it contains the dd offset.
             if uboot_dd is None:
                 cls.uboot_dd = False
@@ -700,7 +701,7 @@
                          boot_device_or_file, k_img_data, i_img_data,
                          d_img_data):
         with cls.hardwarepack_handler:
-            spl_file = cls.get_file('spl')
+            spl_file = cls.get_file('spl_file')
             if cls.spl_in_boot_part:
                 assert spl_file is not None, (
                     "SPL binary could not be found")
@@ -715,7 +716,7 @@
             if cls.spl_dd:
                 cls._dd_file(spl_file, boot_device_or_file, cls.spl_dd)
 
-            uboot_file = cls.get_file('u_boot')
+            uboot_file = cls.get_file('u_boot_file')
             if cls.uboot_dd:
                 cls._dd_file(uboot_file, boot_device_or_file, cls.uboot_dd)
 
@@ -764,7 +765,6 @@
         if is_live:
             parts_dir = 'casper'
         uboot_parts_dir = os.path.join(chroot_dir, parts_dir)
-
         cmd_runner.run(['mkdir', '-p', boot_disk]).wait()
         with partition_mounted(boot_partition, boot_disk):
             if cls.uboot_in_boot_part:
@@ -781,7 +781,7 @@
                     else:
                         default = None
                     # </legacy v1 support>
-                    uboot_bin = cls.get_file('u_boot', default=default)
+                    uboot_bin = cls.get_file('u_boot_file', default=default)
                     assert uboot_bin is not None, (
                         "uboot binary could not be found")
 
@@ -1277,7 +1277,7 @@
         # XXX: delete this method when hwpacks V1 can die
         assert cls.hwpack_format == HardwarepackHandler.FORMAT_1
         with cls.hardwarepack_handler:
-            uboot_file = cls.get_file('u_boot', default=os.path.join(
+            uboot_file = cls.get_file('u_boot_file', default=os.path.join(
                     chroot_dir, 'usr', 'lib', 'u-boot', cls.uboot_flavor,
                     'u-boot.imx'))
             install_mx5_boot_loader(uboot_file, boot_device_or_file,
@@ -1778,7 +1778,7 @@
             default = _get_mlo_file(chroot_dir)
         except AssertionError:
             default = None
-        mlo_file = cls.get_file('spl', default=default)
+        mlo_file = cls.get_file('spl_file', default=default)
         cmd_runner.run(["cp", "-v", mlo_file, boot_disk], as_root=True).wait()
         # XXX: Is this really needed?
         cmd_runner.run(["sync"]).wait()

=== modified file 'linaro_image_tools/media_create/chroot_utils.py'
--- linaro_image_tools/media_create/chroot_utils.py	2012-06-13 14:11:28 +0000
+++ linaro_image_tools/media_create/chroot_utils.py	2012-07-23 14:10:35 +0000
@@ -25,7 +25,7 @@
     is_arm_host,
     find_command,
     )
-
+from linaro_image_tools.media_create.boards import HardwarepackHandler
 
 # It'd be nice if we could use atexit here, but all the things we need to undo
 # have to happen right after install_hwpacks completes and the atexit
@@ -97,7 +97,17 @@
     print "-" * 60
     print "Installing (linaro-hwpack-install) %s in target rootfs." % (
         hwpack_basename)
-    args = ['linaro-hwpack-install']
+
+    # Get infromation required by linaro-hwpack-install
+    with HardwarepackHandler([hwpack_file]) as hwpack:
+        version, _ = hwpack.get_field("version")
+        architecture, _ = hwpack.get_field("architecture")
+        name, _ = hwpack.get_field("name")
+
+    args = ['linaro-hwpack-install',
+            '--hwpack-version', version,
+            '--hwpack-arch', architecture,
+            '--hwpack-name', name]
     if hwpack_force_yes:
         args.append('--force-yes')
     args.append('/%s' % hwpack_basename)

=== modified file 'linaro_image_tools/media_create/tests/test_media_create.py'
--- linaro_image_tools/media_create/tests/test_media_create.py	2012-07-17 15:33:19 +0000
+++ linaro_image_tools/media_create/tests/test_media_create.py	2012-07-23 15:28:30 +0000
@@ -216,22 +216,22 @@
 
     def test_get_metadata(self):
         data = 'data to test'
-        metadata = self.metadata + "TEST=%s\n" % data
+        metadata = self.metadata + "U_BOOT=%s\n" % data
         tarball = self.add_to_tarball(
             [('metadata', metadata)])
         hp = HardwarepackHandler([tarball])
         with hp:
-            test_data, _ = hp.get_field(hp.main_section, 'test')
+            test_data, _ = hp.get_field('u_boot_file')
             self.assertEqual(test_data, data)
 
     def test_preserves_formatters(self):
         data = '%s%d'
-        metadata = self.metadata + "TEST=%s\n" % data
+        metadata = self.metadata + "U_BOOT=%s\n" % data
         tarball = self.add_to_tarball(
             [('metadata', metadata)])
         hp = HardwarepackHandler([tarball])
         with hp:
-            test_data, _ = hp.get_field(hp.main_section, 'test')
+            test_data, _ = hp.get_field('u_boot_file')
             self.assertEqual(test_data, data)
 
     def test_creates_tempdir(self):
@@ -252,15 +252,14 @@
 
     def test_get_file(self):
         data = 'test file contents\n'
-        metadata_file = 'TESTFILE'
         file_in_archive = 'testfile'
-        metadata = self.metadata + "%s=%s\n" % (metadata_file, file_in_archive)
+        metadata = self.metadata + "%s=%s\n" % ('U_BOOT', file_in_archive)
         tarball = self.add_to_tarball(
             [('metadata', metadata),
              (file_in_archive, data)])
         hp = HardwarepackHandler([tarball])
         with hp:
-            test_file = hp.get_file(metadata_file)
+            test_file = hp.get_file('u_boot_file')
             self.assertEquals(data, open(test_file, 'r').read())
 
 
@@ -272,7 +271,7 @@
         def __enter__(self):
             return self
 
-        def get_field(self, section, field):
+        def get_field(self, field):
             try:
                 return self.metadata_dict[field], None
             except:
@@ -367,7 +366,7 @@
         class config(BoardConfig):
             pass
         config.set_metadata('ahwpack.tar.gz')
-        self.assertEquals(data_to_set.split(' '), config.wired_interfaces)
+        self.assertEquals(data_to_set, config.wired_interfaces)
 
     def test_sets_wireless_interfaces(self):
         self.useFixture(MockSomethingFixture(
@@ -382,7 +381,7 @@
         class config(BoardConfig):
             pass
         config.set_metadata('ahwpack.tar.gz')
-        self.assertEquals(data_to_set.split(' '), config.wireless_interfaces)
+        self.assertEquals(data_to_set, config.wireless_interfaces)
 
     def test_sets_mmc_id(self):
         self.useFixture(MockSomethingFixture(
@@ -3225,6 +3224,20 @@
 
 
 class TestInstallHWPack(TestCaseWithFixtures):
+    def create_minimal_v3_hwpack(self, location, name, version, architecture):
+        metadata = "\n".join([
+                    "name: " + name,
+                    "version: " + version,
+                    "architecture: " + architecture,
+                    "format: 3.0"
+                    ])
+        print metadata
+        tar_file = tarfile.open(location, mode='w:gz')
+        tarinfo = tarfile.TarInfo("metadata")
+        tarinfo.size = len(metadata)
+        tar_file.addfile(tarinfo, StringIO(metadata))
+        tar_file.close()
+
     def mock_prepare_chroot(self, chroot_dir, tmp_dir):
         def fake_prepare_chroot(chroot_dir, tmp_dir):
             cmd_runner.run(['prepare_chroot %s %s' % (chroot_dir, tmp_dir)],
@@ -3278,12 +3291,23 @@
             sys, 'stdout', open('/dev/null', 'w')))
         fixture = self.useFixture(MockCmdRunnerPopenFixture())
         chroot_dir = 'chroot_dir'
+        hwpack_dir = tempfile.mkdtemp()
+        hwpack_file_name = 'hwpack.tgz'
+        hwpack_tgz_location = os.path.join(hwpack_dir, hwpack_file_name)
+        hwpack_name = "foo"
+        hwpack_version = "4"
+        hwpack_architecture = "armel"
+        self.create_minimal_v3_hwpack(hwpack_tgz_location, hwpack_name,
+                                      hwpack_version, hwpack_architecture)
         force_yes = False
-        install_hwpack(chroot_dir, 'hwpack.tgz', force_yes)
+        install_hwpack(chroot_dir, hwpack_tgz_location, force_yes)
         self.assertEquals(
-            ['%s cp hwpack.tgz %s' % (sudo_args, chroot_dir),
-             '%s %s %s linaro-hwpack-install /hwpack.tgz'
-                % (sudo_args, chroot_args, chroot_dir)],
+            ['%s cp %s %s' % (sudo_args, hwpack_tgz_location, chroot_dir),
+             '%s %s %s linaro-hwpack-install --hwpack-version %s '
+             '--hwpack-arch %s --hwpack-name %s /%s'
+                % (sudo_args, chroot_args, chroot_dir,
+                   hwpack_version, hwpack_architecture, hwpack_name,
+                   hwpack_file_name)],
             fixture.mock.commands_executed)
 
         fixture.mock.calls = []
@@ -3303,9 +3327,23 @@
 
         prefer_dir = preferred_tools_dir()
 
+        hwpack_dir = tempfile.mkdtemp()
+        hwpack_file_names = ['hwpack1.tgz', 'hwpack2.tgz']
+        hwpack_tgz_locations = []
+        hwpack_names = []
+        for hwpack_file_name in hwpack_file_names:
+            hwpack_tgz_location = os.path.join(hwpack_dir, hwpack_file_name)
+            hwpack_tgz_locations.append(hwpack_tgz_location)
+            hwpack_names.append(hwpack_file_name)
+            hwpack_version = "4"
+            hwpack_architecture = "armel"
+            self.create_minimal_v3_hwpack(
+                hwpack_tgz_location, hwpack_file_name, hwpack_version,
+                hwpack_architecture)
+
         install_hwpacks(
-            chroot_dir, tmp_dir, prefer_dir, force_yes, [], 'hwpack1.tgz',
-            'hwpack2.tgz')
+            chroot_dir, tmp_dir, prefer_dir, force_yes, [],
+            hwpack_tgz_locations[0], hwpack_tgz_locations[1])
         linaro_hwpack_install = find_command(
             'linaro-hwpack-install', prefer_dir=prefer_dir)
         expected = [
@@ -3313,19 +3351,27 @@
             'cp %(linaro_hwpack_install)s %(chroot_dir)s/usr/bin',
             'mount proc %(chroot_dir)s/proc -t proc',
             'chroot %(chroot_dir)s true',
-            'cp hwpack1.tgz %(chroot_dir)s',
-            ('%(chroot_args)s %(chroot_dir)s linaro-hwpack-install '
-             '--force-yes /hwpack1.tgz'),
-            'cp hwpack2.tgz %(chroot_dir)s',
-            ('%(chroot_args)s %(chroot_dir)s linaro-hwpack-install '
-             '--force-yes /hwpack2.tgz'),
+            'cp %(hwpack1)s %(chroot_dir)s',
+            ('%(chroot_args)s %(chroot_dir)s linaro-hwpack-install '
+             '--hwpack-version %(hp_version)s '
+             '--hwpack-arch %(hp_arch)s --hwpack-name %(hp_name1)s'
+             ' --force-yes /hwpack1.tgz'),
+            'cp %(hwpack2)s %(chroot_dir)s',
+            ('%(chroot_args)s %(chroot_dir)s linaro-hwpack-install '
+             '--hwpack-version %(hp_version)s '
+             '--hwpack-arch %(hp_arch)s --hwpack-name %(hp_name2)s'
+             ' --force-yes /hwpack2.tgz'),
             'rm -f %(chroot_dir)s/hwpack2.tgz',
             'rm -f %(chroot_dir)s/hwpack1.tgz',
             'umount -v %(chroot_dir)s/proc',
             'rm -f %(chroot_dir)s/usr/bin/linaro-hwpack-install']
         keywords = dict(
             chroot_dir=chroot_dir, tmp_dir=tmp_dir, chroot_args=chroot_args,
-            linaro_hwpack_install=linaro_hwpack_install)
+            linaro_hwpack_install=linaro_hwpack_install,
+            hwpack1=hwpack_tgz_locations[0],
+            hwpack2=hwpack_tgz_locations[1],
+            hp_version=hwpack_version, hp_name1=hwpack_names[0],
+            hp_name2=hwpack_names[1], hp_arch=hwpack_architecture)
         expected = [
             "%s %s" % (sudo_args, line % keywords) for line in expected]
         self.assertEquals(expected, fixture.mock.commands_executed)

