=== added file 'fetch_image_ui.py'
@@ -0,0 +1,1661 @@
+#!/usr/bin/env python
+# Copyright (C) 2010, 2011 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 wx
+import wx.wizard
+import wx.wizard as wiz
+import sys
+import re
+import os
+import linaro_image_tools.FetchImage as FetchImage
+import string
+import unittest
+import operator
+import Queue
+
+
+def add_button(bind_to,
+ sizer,
+ label,
+ style,
+ select_event,
+ hover_event,
+ unhover_event):
+
+ """Create a radio button with event bindings."""
+ if(style != None):
+ radio_button = wx.RadioButton(bind_to, label=label, style=style)
+ else:
+ radio_button = wx.RadioButton(bind_to, label=label)
+
+ sizer.Add(radio_button, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)
+ bind_to.Bind(wx.EVT_RADIOBUTTON, select_event, radio_button)
+ wx.EVT_ENTER_WINDOW(radio_button, hover_event)
+ wx.EVT_LEAVE_WINDOW(radio_button, unhover_event)
+
+ return radio_button
+
+
+class ReleaseOrSnapshotPage(wiz.PyWizardPage):
+ """Ask the user if they want to use a release or a snapshot"""
+
+ def __init__(self, parent, config):
+ wiz.PyWizardPage.__init__(self, parent)
+ self.config = config
+ self.settings = self.config.settings
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ self.next = None
+ self.prev = None
+
+ self.sizer.Add(wx.StaticText(self, -1,
+"""This Wizard will write an operating system of your choosing to
+either a disk image or to an MMC card. First we need to know if
+your priority is stability or the latest and greatest features."""))
+
+ self.box1 = wx.BoxSizer(wx.VERTICAL)
+
+ self.button_text = {'release': "I would like to run stable, "
+ "tested software.",
+ 'snapshot': "I would like to run untested, but "
+ "more up-to-date software."}
+
+ add_button(self, self.box1, self.button_text['release'],
+ wx.RB_GROUP, self.event_radio_button_select, None, None)
+
+ # Save the setting for the default selected value
+ self.settings['release_or_snapshot'] = "release"
+
+ add_button(self, self.box1, self.button_text['snapshot'], None,
+ self.event_radio_button_select, None, None)
+
+ self.sizer.Add(self.box1, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ self.SetSizerAndFit(self.sizer)
+ self.sizer.Fit(self)
+ self.Move((50, 50))
+
+ def event_radio_button_select(self, event):
+ self.radio_selected = event.GetEventObject().GetLabel()
+ # The radio button can be release, snapshot or "latest snapshot"
+ if(self.radio_selected == self.button_text['release']):
+ self.settings['release_or_snapshot'] = "release"
+ else:
+ self.settings['release_or_snapshot'] = "snapshot"
+
+ def SetNext(self, next):
+ self.next = next
+
+ def GetNext(self):
+ return self.next
+
+
+class AboutMyHardwarePage(wiz.WizardPageSimple):
+ """Ask the user about their hardware. This only asks about the board, not
+ any specific hardware packs because there can be multiple names for the
+ same hardware pack or sometimes a hardware pack is only available in the
+ releases or snapshots repository. We whittle down the choice as we go
+ and the user can chose a hardare pack (if they don't like the default)
+ under advanced options in the Linaro Media Create options
+ page"""
+
+ def __init__(self, parent, config, db, width):
+ wiz.WizardPageSimple.__init__(self, parent)
+ self.settings = config.settings
+ self.db = db
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ self.box1 = wx.BoxSizer(wx.VERTICAL)
+ self.box2 = wx.BoxSizer(wx.VERTICAL)
+
+ header = wx.StaticText(self,
+ label = "Please select the hardware that you "
+ "would like to build an image for from "
+ "the following list")
+
+ header.Wrap(width - 10) # -10 because boarder below is 5 pixels wide
+
+ #--- Hardware Combo Box ---
+ # Make sure that the displayed release is the one set in settings if
+ # no selection is made
+ if "panda" in self.settings['choice']['hardware'].keys():
+ default_hardware = "panda"
+ else:
+ default_hardware = self.settings['choice']['hardware'].keys()[-1]
+
+ self.settings['hardware'] = default_hardware
+ self.settings['compatable_hwpacks'] = (
+ self.settings['choice']['hwpack'][self.settings['hardware']])
+
+ self.cb_hardware = wx.ComboBox(self,
+ value =
+ self.settings['choice']['hardware'][default_hardware],
+ style = wx.CB_DROPDOWN | wx.CB_READONLY)
+
+ self.Bind(wx.EVT_COMBOBOX,
+ self.event_combo_box_hardware,
+ self.cb_hardware)
+ self.box1.Add(self.cb_hardware, 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)
+
+ self.sizer.Add(header)
+ self.sizer.Add(self.box1, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+ self.sizer.Add(self.box2, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+ self.SetSizerAndFit(self.sizer)
+ self.sizer.Fit(self)
+ self.Move((50, 50))
+
+ def on_page_changing(self):
+ self.update_hardware_box()
+
+ def update_hardware_box(self):
+ self.cb_hardware.Clear()
+
+ sorted_hardware_names = sorted(self.settings['choice']['hardware']
+ .iteritems(),
+ key=operator.itemgetter(1))
+
+ table = self.settings['release_or_snapshot'] + "_hwpacks"
+
+ for device_name, human_readable_name in sorted_hardware_names:
+ for hwpack in self.settings['choice']['hwpack'][device_name]:
+ if self.db.hardware_is_available_in_table(table, hwpack):
+ self.cb_hardware.Append(human_readable_name, device_name)
+ break
+
+ #--- Event(s) ---
+ def event_combo_box_hardware(self, event):
+ self.settings['hardware'] = (event
+ .GetEventObject()
+ .GetClientData(event.GetSelection())
+ .encode('ascii'))
+
+ self.settings['compatable_hwpacks'] = (
+ self.settings['choice']['hwpack'][self.settings['hardware']])
+ #--- END event(s) ---
+
+
+class SelectStableRelease(wiz.WizardPageSimple):
+ """Ask the user which Linaro release they would like to run."""
+ def __init__(self, parent, config, db, width):
+ wiz.WizardPageSimple.__init__(self, parent)
+ self.settings = config.settings
+ self.db = db
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ self.wizard = parent
+
+ header = wx.StaticText(self, label = "Please select the stable Linaro "
+ "release you would like to use")
+
+ header.Wrap(width - 10) # -10 because boarder below is 5 pixels wide
+
+ self.sizer.Add(header)
+ self.box1 = wx.BoxSizer(wx.VERTICAL)
+
+ platforms = []
+ for key, value in self.settings['choice']['platform'].items():
+ platforms.append(key)
+
+ default_release = self.settings['UI']['translate'][platforms[-1]]
+ self.cb_release = wx.ComboBox(self,
+ value = default_release,
+ style = wx.CB_DROPDOWN | wx.CB_READONLY)
+ self.Bind(wx.EVT_COMBOBOX,
+ self.event_combo_box_release,
+ self.cb_release)
+
+ if(default_release in self.settings['UI']['translate']):
+ default_release = self.settings['UI']['translate'][default_release]
+ self.settings['platform'] = (
+ self.settings['UI']['reverse-translate'][default_release])
+
+ for item in platforms:
+ if(item in self.settings['UI']['translate']):
+ new_item = self.settings['UI']['translate'][item]
+ item = new_item
+
+ self.cb_release.Append(item, item.upper())
+
+ self.cb_build = wx.ComboBox(self,
+ style = wx.CB_DROPDOWN | wx.CB_READONLY)
+ self.Bind(wx.EVT_COMBOBOX, self.event_combo_box_build, self.cb_build)
+
+ self.box1.Add(self.cb_release, 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)
+ self.box1.Add(self.cb_build, 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)
+ self.sizer.Add(self.box1, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+ self.SetSizerAndFit(self.sizer)
+ self.sizer.Fit(self)
+ self.Move((50, 50))
+
+ def update_build_box(self):
+ """Depending on what hardware has been chosen, the OS list may be
+ restricted. Filter out anything that is unavailable."""
+ self.cb_build.Clear()
+
+ builds = self.db.get_builds(self.settings['platform'])
+ self.cb_build.SetValue("No build available")
+
+ for build in builds:
+ if( self.db.hardware_is_available_for_platform_build(
+ self.settings['compatable_hwpacks'],
+ self.settings['platform'],
+ build)
+ and self.db.build_is_available_for_platform_image(
+ "release_binaries",
+ self.settings['platform'],
+ self.settings['image'],
+ build)):
+
+ self.cb_build.Append(build)
+ self.cb_build.SetValue(build)
+ self.settings['release_build'] = build
+
+ available_hwpacks = (
+ self.db.get_available_hwpacks_for_hardware_build_plaform(
+ self.settings['compatable_hwpacks'],
+ self.settings['platform'],
+ self.settings['release_build']))
+
+ if len(available_hwpacks):
+ self.settings['hwpack'] = available_hwpacks[0]
+ self.wizard.FindWindowById(wx.ID_FORWARD).Enable()
+ else:
+ self.wizard.FindWindowById(wx.ID_FORWARD).Disable()
+
+ def update_release_and_build_boxes(self):
+ """Depending on what hardware has been chosen, some builds may be
+ unavailable..."""
+ self.cb_release.Clear()
+
+ default_release = None
+ for platform, value in self.settings['choice']['platform'].items():
+ if(self.db.hardware_is_available_for_platform(
+ self.settings['compatable_hwpacks'],
+ platform)
+ and len(self.db.execute_return_list(
+ 'select * from release_binaries '
+ 'where platform == ? and image == ?',
+ (platform, self.settings['image'])))):
+
+ if(platform in self.settings['UI']['translate']):
+ platform = self.settings['UI']['translate'][platform]
+
+ self.cb_release.Append(platform, platform.upper())
+ if not default_release or default_release < platform:
+ default_release = platform
+
+ self.settings['platform'] = (
+ self.settings['UI']['reverse-translate'][default_release])
+ self.cb_release.SetValue(default_release)
+ self.update_build_box()
+
+ #--- Event(s) ---
+ def event_combo_box_release(self, evt):
+ str = evt.GetString().encode('ascii').lower()
+ if(str in self.settings['UI']['reverse-translate']):
+ str = self.settings['UI']['reverse-translate'][str]
+ self.settings['platform'] = str
+
+ self.update_build_box()
+
+ def event_combo_box_build(self, evt):
+ self.settings['release_build'] = evt.GetString().encode('ascii')
+ #--- END event(s) ---
+
+
+class SelectSnapshot(wiz.WizardPageSimple):
+ """Present the user with a calendar widget and a list of builds available
+ on the selected date so they can chose a snapshot. Filter out days when
+ their chosen hardware does not have an available build."""
+
+ def __init__(self, parent, config, db, width):
+ wiz.WizardPageSimple.__init__(self, parent)
+ self.settings = config.settings
+ self.db = db
+ self.wizard = parent
+ self.width = width
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+
+ header = wx.StaticText(self,
+ label = "Builds are created most days. First "
+ "please select the day on which the "
+ "build you would like to use was built,"
+ " then, if there was more than one "
+ "build that day you will be able to "
+ "select the build number.")
+ header.Wrap(width - 10) # -10 because boarder below is 5 pixels wide
+
+ box1 = wx.BoxSizer(wx.VERTICAL)
+ self.sizer.Add(header)
+
+ # Set today as the default build date in settings
+ # (matches the date picker)
+ self.today = wx.DateTime()
+ self.today.SetToCurrent()
+ self.settings['build_date'] = self.today.FormatISODate().encode('ascii')
+
+ dpc = wx.DatePickerCtrl(self, size = (120, -1),
+ style = wx.DP_DEFAULT)
+ self.Bind(wx.EVT_DATE_CHANGED, self.on_date_changed, dpc)
+
+ #--- Build number Combo Box ---
+ # Make sure that the displayed build is the one set in settings if no
+ # selection is made
+ self.settings['build_number'] = 0
+ self.update_build()
+ self.cb_build = wx.ComboBox(self,
+ style = wx.CB_DROPDOWN | wx.CB_READONLY)
+ self.Bind(wx.EVT_COMBOBOX, self.event_combo_box_build, self.cb_build)
+
+ #--- Layout ---
+ # -- Combo boxes for hardware and image selection --
+
+ grid2 = wx.FlexGridSizer(0, 2, 0, 0)
+ grid2.Add(dpc, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+ grid2.Add(self.cb_build, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ box1.Add(grid2, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ self.sizer.Add(box1, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ self.help_text = wx.StaticText(self)
+ self.sizer.Add(self.help_text, 1, wx.EXPAND, 5)
+
+ self.SetSizer(self.sizer)
+ self.sizer.Fit(self)
+ self.Move((50, 50))
+
+ def update_platform(self):
+ build_and_date = self.settings['snapshot_build'].split(":")
+
+ if len(build_and_date) == 2:
+ self.settings['platform'] = (
+ self.db.execute_return_list(
+ "select platform from snapshot_binaries "
+ "where date == ? and build == ?",
+ (build_and_date[0], build_and_date[1])))
+
+ if len(self.settings['platform']) > 0:
+ self.settings['platform'] = self.settings['platform'][0][0]
+
+ def update_build(self):
+ small_date = re.sub('-', '', self.settings['build_date'])
+ self.settings['snapshot_build'] = (small_date
+ + ":"
+ + str(self.settings['build_number']))
+
+ def fill_build_combo_box_for_date(self, date):
+ """Every time a date is chosen, this function should be called. It will
+ check to see if a compatible build is available. If there isn't, it
+ will search for one and provide some help text to tell the user when
+ compatable builds were built."""
+ # Re-populate the build combo box
+
+ self.cb_build.Clear()
+
+ builds = self.db.get_binary_builds_on_day_from_db(
+ self.settings['image'],
+ date,
+ self.settings['compatable_hwpacks'])
+
+ if len(builds):
+ max = 0
+ for item in builds:
+ #Always get a tuple, only interested in the first entry
+ item = item[0]
+ self.cb_build.Append(item, item.upper())
+
+ if item > max:
+ max = item
+
+ self.cb_build.SetValue(max)
+ self.wizard.FindWindowById(wx.ID_FORWARD).Enable()
+ self.help_text.SetLabel("")
+
+ else:
+ self.cb_build.SetValue("No builds available")
+ future_date, past_date = self.db.get_next_prev_day_with_builds(
+ self.settings['image'],
+ date,
+ self.settings['compatable_hwpacks'])
+
+ help_text = None
+
+ if future_date and past_date:
+ help_text = ("There are no builds that match your "
+ "specifications available on the selected date. "
+ "The previous build was on " + past_date +
+ " and the next build was on " + future_date + ".")
+ elif future_date:
+ help_text = ("There are no builds that match your "
+ "specifications available on the selected date. "
+ "The next build was on " + future_date +
+ " and I couldn't find a past build (looked one "
+ "year back from the selected date).")
+ elif past_date:
+ help_text = ("There are no builds that match your "
+ "specifications available on the selected date. "
+ "The previous build was on " + past_date)
+ if date != self.today.FormatISODate().encode('ascii'):
+ help_text += (" and I couldn't find a future build (I "
+ "looked up to one year forward from the "
+ "selected date).")
+ else:
+ help_text = ("I could not find any builds that match your "
+ "specifications close to the selected date (I "
+ "looked forward and back one year from the "
+ "selected date).")
+
+ self.help_text.SetLabel(help_text)
+ self.help_text.Wrap(self.width - 10)
+ self.wizard.FindWindowById(wx.ID_FORWARD).Disable()
+
+ #--- Event(s) ---
+ def on_date_changed(self, evt):
+ self.settings['build_date'] = evt.GetDate().FormatISODate().encode('ascii')
+ self.fill_build_combo_box_for_date(self.settings['build_date'])
+ self.update_build()
+
+ def event_combo_box_build(self, evt):
+ self.settings['build_number'] = evt.GetString().encode('ascii').lower()
+ self.update_build()
+ #--- END event(s) ---
+
+
+class SelectOS(wiz.WizardPageSimple):
+ """Ask the user which OS they would like to run. Filter out any choices
+ that are unavailable due to previous choices."""
+ def __init__(self, parent, config, db, width):
+ wiz.WizardPageSimple.__init__(self, parent)
+ self.settings = config.settings
+ self.wizard = parent
+ self.db = db
+ self.width = width
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ self.settings['image'] = None
+
+ header = wx.StaticText(self, label = "Please select the operating "
+ "system you would like to run on "
+ "your hardware.")
+ header.Wrap(width - 10) # -10 because boarder below is 5 pixels wide
+
+ self.box1 = wx.BoxSizer(wx.VERTICAL)
+ self.sizer.Add(header)
+
+ self.cb_image = wx.ComboBox(self,
+ style = wx.CB_DROPDOWN | wx.CB_READONLY)
+ self.Bind(wx.EVT_COMBOBOX, self.event_combo_box_os, self.cb_image)
+
+ #--- Layout ---
+ # -- Combo boxes for hardware and image selection --
+ self.box1.Add(self.cb_image, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ self.sizer.Add(self.box1, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ self.help_text = wx.StaticText(self)
+ self.sizer.Add(self.help_text, 1, wx.EXPAND, 5)
+
+ self.SetSizer(self.sizer)
+ self.sizer.Fit(self)
+ self.Move((50, 50))
+
+ def get_human_os_name(self, item):
+ """Given an OS name from the database, return a human name (either
+ translated from the YAML settings, or just prettified) and if it is a
+ LEB OS or not"""
+
+ item = re.sub("linaro-", "", item) # Remove any linaro- decoration
+
+ human_name = item
+
+ if item in self.settings['UI']['descriptions']:
+ human_name = self.settings['UI']['descriptions'][item]
+ else:
+ # Make human_name look nicer...
+ human_name = string.capwords(item)
+
+ leb_search = re.search("^LEB:\s*(.*)$", human_name)
+
+ if leb_search:
+ return leb_search.group(1), True
+
+ return human_name, False
+
+ def fill_os_list(self):
+ """Filter the list of OS's from the config file based on the users
+ preferences so all choices in the list are valid (i.e. their hardware
+ is supported for the build they have chosen)."""
+
+ # select unique image from snapshot_binaries/release_binaries to
+ # generate list
+ os_list = None
+ if self.settings['release_or_snapshot'] == "release":
+ os_list = self.db.get_os_list_from('release_binaries')
+ else:
+ os_list = self.db.get_os_list_from('snapshot_binaries')
+
+ self.cb_image.Clear()
+
+ printed_tag = None
+ last_name = None
+ current_image_setting_valid = False
+
+ for state in ["LEB", "other"]:
+ for item in os_list:
+ if item == "old":
+ # Old is a directory that sometimes hangs around,
+ # but isn't one we want to display
+ continue
+
+ # Save the original, untouched image name for use later.
+ # We give it a more human name for display
+ original = item
+ item = re.sub("linaro-", "", item)
+
+ os_hardware_combo_available = (
+ self.db.image_hardware_combo_available(
+ self.settings['release_or_snapshot'],
+ original,
+ self.settings['compatable_hwpacks']))
+
+ if os_hardware_combo_available:
+ human_name, is_LEB = self.get_human_os_name(item)
+
+ if item == self.settings['image']:
+ current_image_setting_valid = True
+
+ if state == "LEB" and is_LEB:
+
+ if printed_tag != state:
+ self.cb_image.Append(
+ "- Linaro Supported Releases -")
+ printed_tag = state
+
+ self.cb_image.Append(human_name, original)
+
+ if self.settings['image'] == None:
+ self.settings['image'] = original
+
+ elif state != "LEB" and not is_LEB:
+ if printed_tag != state:
+ self.cb_image.Append(
+ "- Community Supported Releases -")
+ printed_tag = state
+
+ self.cb_image.Append(human_name, original)
+
+ last_name = original
+
+ if( self.settings['image'] != None
+ and current_image_setting_valid == False):
+ # If we have an image setting, but it doesn't match the OS list, we
+ # have switched OS list. It may be that adding/removing "linaro-"
+ # from the name will get a match.
+
+ if re.search("linaro-", self.settings['image']):
+ test_name = re.sub("linaro-", "", self.settings['image'])
+ else:
+ test_name = "linaro-" + self.settings['image']
+
+ if test_name in os_list:
+ # Success! We have translated the name and can retain the
+ # "old setting"
+ self.settings['image'] = test_name
+ current_image_setting_valid = True
+
+ if( self.settings['image'] == None
+ or current_image_setting_valid == False):
+ # This should only get hit if there are no LEBs available
+ self.settings['image'] = last_name
+
+ assert self.settings['image']
+
+ # Make sure the visible selected value matches the saved setting
+ self.cb_image.SetValue(
+ self.get_human_os_name(self.settings['image'])[0])
+
+ #--- Event(s) ---
+ def event_combo_box_os(self, evt):
+ self.settings['image'] = self.cb_image.GetClientData(
+ evt.GetSelection())
+
+ if self.settings['image']: # Is None for items that aren't an OS
+ self.wizard.FindWindowById(wx.ID_FORWARD).Enable()
+ image = re.sub("linaro-", "", self.settings['image'])
+
+ if image + "::long" in self.settings['UI']['descriptions']:
+ self.help_text.SetLabel(self.settings['UI']
+ ['descriptions']
+ [image + "::long"])
+ else:
+ self.help_text.SetLabel("")
+
+ else: # Have selected help text
+ self.wizard.FindWindowById(wx.ID_FORWARD).Disable()
+ self.help_text.SetLabel("Please select an operating system to run "
+ "on your chosen hardware.")
+
+ self.help_text.Wrap(self.width - 10)
+ #--- END event(s) ---
+
+
+class LMC_settings(wiz.WizardPageSimple):
+ """Present the user with, intially, the choice of writing the file system
+ they are going to have created to a file, or directly to a device. Ask
+ which file/device to write to.
+
+ If writing to a device, the user is asked to tick a box saying that they
+ understand that the device they have chosen will be erased.
+
+ If the user ticks the advanced box, more options are shown."""
+
+ def __init__(self, parent, config, db, width):
+ wiz.WizardPageSimple.__init__(self, parent)
+ self.settings = config.settings
+ self.wizard = parent
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ self.yes_use_mmc = False
+ self.db = db
+
+ self.settings['path_selected'] = ""
+
+ header = wx.StaticText(self,
+ label = "Media Creation Settings\n\n"
+ "Please select if you would like to write the "
+ "file system I am about to create to a memory "
+ "card, or to a file on the local file system.")
+ header.Wrap(width - 10) # -10 because boarder below is 5 pixels wide
+
+ #--- Build some widgets ---
+ #-- Target file system --
+ file_systems = ["ext3", "ext4", "btrfs", "ext2"]
+ default_target = file_systems[0]
+ self.settings['rootfs'] = default_target
+ cb_rootfs = wx.ComboBox(self,
+ value = default_target,
+ style = wx.CB_DROPDOWN | wx.CB_READONLY)
+
+ for item in file_systems:
+ cb_rootfs.Append(item, item.upper())
+
+ self.Bind(wx.EVT_COMBOBOX, self.event_combo_box_rootfs, cb_rootfs)
+
+ #-- Image size spinner
+ self.image_size_spinner = wx.SpinCtrl(self, -1, "")
+ self.Bind(wx.EVT_SPINCTRL,
+ self.event_image_size,
+ self.image_size_spinner)
+
+ #-- Swap size spinner
+ self.swap_size_spinner = wx.SpinCtrl(self, -1, "")
+ self.Bind(wx.EVT_SPINCTRL,
+ self.event_swap_size,
+ self.swap_size_spinner)
+
+ #--- Layout ---
+ self.sizer.Add(header, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+ box1 = wx.BoxSizer(wx.VERTICAL)
+ file_dev_grid = wx.FlexGridSizer(0, 2, 0, 0)
+ box1.Add(file_dev_grid, 0, wx.EXPAND)
+ grid1 = wx.FlexGridSizer(0, 2, 0, 0)
+
+ # self.settings['write_to_file_or_device'] should match the first
+ # button below...
+ self.settings['write_to_file_or_device'] = "file"
+ add_button(self,
+ file_dev_grid,
+ "Write to file",
+ wx.RB_GROUP,
+ self.event_radio_button_select,
+ None, None)
+
+ add_button(self,
+ file_dev_grid,
+ "Write to device",
+ None,
+ self.event_radio_button_select,
+ None, None)
+
+ self.help_text_values = {"device": "Please select a device to write "
+ "the file system to:",
+ "file": "Please select a file to write the "
+ "file system to:"}
+
+ self.help_text = wx.StaticText(
+ self,
+ label =
+ self.help_text_values[
+ self.settings['write_to_file_or_device']])
+ self.help_text.Wrap(width - 10)
+
+ #-- File/dev picker --
+ file_browse_button = wx.Button(self, -1, "Browse")
+ file_browse_grid = wx.FlexGridSizer(0, 2, 0, 0)
+ self.file_path_and_name = wx.TextCtrl(self, -1, "", size=(300, -1))
+
+ file_browse_grid.Add(self.file_path_and_name, 0, wx.EXPAND)
+ file_browse_grid.Add(file_browse_button, 0, wx.EXPAND)
+
+ self.Bind(wx.EVT_BUTTON,
+ self.event_open_file_control,
+ file_browse_button)
+
+ self.Bind(wx.EVT_TEXT,
+ self.event_file_path_and_name,
+ self.file_path_and_name)
+
+ box1.Add(self.help_text, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ box1.Add(file_browse_grid, 0, wx.EXPAND)
+
+ cb1 = wx.CheckBox(self, -1, "Show advanced options")
+ self.Bind(wx.EVT_CHECKBOX, self.event_show_advanced_options, cb1)
+ box1.Add(cb1)
+
+ #-- Combo boxes for hardware and image selection --
+ optional_settings_box_title = wx.StaticBox(
+ self,
+ label = " Optional Settings ")
+
+ self.optional_settings_box = wx.StaticBoxSizer(
+ optional_settings_box_title,
+ wx.VERTICAL)
+
+ self.box2 = wx.BoxSizer(wx.VERTICAL)
+
+ self.box2.AddWindow(self.optional_settings_box,
+ 0,
+ border=2,
+ flag=wx.ALL | wx.EXPAND)
+
+ grid1.Add(cb_rootfs, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)
+
+ grid1.Add(wx.StaticText(self,
+ label = "The root file system of the image"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ # We want to sub-devide the cell, to add another grid sizer...
+ file_size_grid = wx.FlexGridSizer(0, 2, 0, 0)
+
+ grid1.Add(file_size_grid,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP)
+
+ # Add a spinner that allows us to type/click a numerical value (defined above)
+ file_size_grid.Add(self.image_size_spinner,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ # Add a choice of MB or GB for size input
+ units = ["GB", "MB"]
+ self.size_unit = units[0] # Set the default unit
+ unit_choice = wx.Choice(self, -1, (100, 50), choices = units)
+ self.Bind(wx.EVT_CHOICE, self.event_chose_unit, unit_choice)
+ file_size_grid.Add(unit_choice, 0, wx.ALIGN_RIGHT | wx.TOP, 5)
+
+ # Back out of the extra grid, add some help text
+ grid1.Add(wx.StaticText(
+ self,
+ label = "Writing to file only: Image file size"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ # The swap size (MB only)
+ grid1.Add(self.swap_size_spinner,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ grid1.Add(wx.StaticText(self, label = "Swap file size in MB"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.cb_hwpacks = wx.ComboBox(
+ self,
+ value = self.settings['compatable_hwpacks'][0],
+ style = wx.CB_DROPDOWN | wx.CB_READONLY)
+
+ self.Bind(wx.EVT_COMBOBOX,
+ self.event_combo_box_hwpack,
+ self.cb_hwpacks)
+
+ grid1.Add(self.cb_hwpacks,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ grid1.Add(wx.StaticText(self, label = "Compatible hardware packs"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.optional_settings_box.Add(grid1, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ confirm_mmc_usage_title = wx.StaticBox(self, label = " Are you sure? ")
+
+ self.confirm_mmc_usage_box = wx.StaticBoxSizer(confirm_mmc_usage_title,
+ wx.VERTICAL)
+ cb2 = wx.CheckBox(
+ self,
+ -1,
+ "Yes, erase and use the device I have selected above.")
+
+ self.Bind(wx.EVT_CHECKBOX, self.event_use_mmc_tickbox, cb2)
+ self.confirm_mmc_usage_box.Add(cb2)
+
+ self.box3 = wx.BoxSizer(wx.VERTICAL)
+ self.box3.AddWindow(self.confirm_mmc_usage_box,
+ 0,
+ border=2,
+ flag=wx.ALL | wx.EXPAND)
+
+ self.sizer.Add(box1, 0, wx.ALIGN_LEFT | wx.ALL, 0)
+ self.sizer.Add(self.box2, 0, wx.ALIGN_LEFT | wx.ALL, 0)
+ self.sizer.Add(self.box3, 0, wx.ALIGN_LEFT | wx.ALL, 0)
+ self.SetSizer(self.sizer)
+ self.sizer.Fit(self)
+ self.Move((50, 50))
+
+ def on_activate(self):
+ self.update_forward_active_and_mmc_confirm_box_visible()
+ self.set_hwpacks_for_hardware()
+
+ def set_hwpacks_for_hardware(self):
+ self.cb_hwpacks.Clear()
+
+ if self.settings['release_or_snapshot'] == "snapshot":
+ self.settings['build'] = self.settings['snapshot_build']
+
+ date_and_build = self.settings['build'].split(":")
+
+ compatable_hwpacks = (
+ self.db.get_available_hwpacks_for_hardware_snapshot_build(
+ self.settings['compatable_hwpacks'],
+ self.settings['platform'],
+ date_and_build[0],
+ date_and_build[1]))
+ else:
+ self.settings['build'] = self.settings['release_build']
+ compatable_hwpacks = (
+ self.db.get_available_hwpacks_for_hardware_build_plaform(
+ self.settings['compatable_hwpacks'],
+ self.settings['platform'],
+ self.settings['build']))
+
+ for hwpack in compatable_hwpacks:
+ self.cb_hwpacks.Append(hwpack)
+
+ self.cb_hwpacks.SetStringSelection(compatable_hwpacks[0])
+ self.settings['hwpack'] = compatable_hwpacks[0]
+
+ def update_forward_active_and_mmc_confirm_box_visible(self):
+ if( self.settings['path_selected']
+ and self.settings['path_selected'] != ""):
+
+ if ( self.settings['write_to_file_or_device'] == "file"
+ or self.settings['write_to_file_or_device'] == "device"
+ and self.yes_use_mmc):
+ self.wizard.FindWindowById(wx.ID_FORWARD).Enable()
+ else:
+ self.wizard.FindWindowById(wx.ID_FORWARD).Disable()
+ else:
+ self.wizard.FindWindowById(wx.ID_FORWARD).Disable()
+
+ if self.settings['write_to_file_or_device'] == "device":
+ self.box3.Show(self.confirm_mmc_usage_box, True)
+ else:
+ self.box3.Hide(self.confirm_mmc_usage_box, True)
+
+ # --- Event Handlers ---
+ def event_open_file_control(self, event):
+ if self.settings['write_to_file_or_device'] == "file":
+
+ dlg = wx.FileDialog(self,
+ message="Save file as ...",
+ defaultDir=os.getcwd(),
+ defaultFile="",
+ style=wx.SAVE)
+
+ elif self.settings['write_to_file_or_device'] == "device":
+ dlg = wx.FileDialog(self,
+ message="Choose a device",
+ defaultDir=os.getcwd(),
+ defaultFile="",
+ style=wx.OPEN | wx.CHANGE_DIR)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ self.settings['path_selected'] = dlg.GetPaths()[0]
+ self.file_path_and_name.SetValue(self.settings['path_selected'])
+
+ dlg.Destroy()
+ self.update_forward_active_and_mmc_confirm_box_visible()
+
+ def event_file_path_and_name(self, event):
+ self.settings['path_selected'] = event.GetString()
+ self.update_forward_active_and_mmc_confirm_box_visible()
+
+ def event_combo_box_hwpack(self, event):
+ self.settings['hwpack'] = event.GetString().encode('ascii')
+
+ def event_combo_box_rootfs(self, evt):
+ self.settings['rootfs'] = evt.GetString().encode('ascii').lower()
+
+ def event_radio_button_select(self, event):
+ """Search the label of the button that has been selected to work out
+ what we are writing to."""
+ setting_search = re.search(
+ "write to (\w+)",
+ event
+ .GetEventObject()
+ .GetLabel()
+ .encode('ascii')
+ .lower())
+
+ assert setting_search
+
+ self.settings['write_to_file_or_device'] = setting_search.group(1)
+
+ self.help_text.SetLabel(
+ self.help_text_values[self.settings['write_to_file_or_device']])
+
+ self.update_forward_active_and_mmc_confirm_box_visible()
+
+ def event_show_advanced_options(self, event):
+ if event.IsChecked():
+ self.box2.Show(self.optional_settings_box, True)
+ else:
+ self.box2.Hide(self.optional_settings_box, True)
+
+ def event_pick_file_path(self, evt):
+ self.settings['path_selected'] = os.path.abspath(evt.GetPath())
+ self.update_forward_active_and_mmc_confirm_box_visible()
+
+ def update_image_size_setting(self):
+ if(self.image_size_spinner.GetValue() > 0):
+ self.settings['image_size'] = (str(self.image_size_spinner
+ .GetValue())
+ + self.size_unit[0])
+ else:
+ self.settings['image_size'] = None
+
+ def event_image_size(self, event):
+ self.update_image_size_setting()
+
+ def event_chose_unit(self, event):
+ self.size_unit = event.GetString()
+ self.update_image_size_setting()
+
+ def event_swap_size(self, event):
+ self.settings['swap_file'] = str(self.image_size_spinner.GetValue())
+
+ def event_use_mmc_tickbox(self, event):
+ self.yes_use_mmc = event.IsChecked()
+ self.update_forward_active_and_mmc_confirm_box_visible()
+
+
+class RunLMC(wiz.WizardPageSimple):
+ """Present the user with some information about their choices and a button
+ to start linaro-media-create. The linaro-media-create process is started in
+ a new thread and important events are communicated back to the UI through a
+ queue."""
+
+ def __init__(self, parent, config, db, width):
+ wiz.WizardPageSimple.__init__(self, parent)
+ self.settings = config.settings
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ self.db = db
+ self.width = width
+ self.wizard = parent
+
+ header = wx.StaticText(self, label = """Installing...""")
+ header.Wrap(width - 10) # -10 because boarder below is 5 pixels wide
+
+ self.sizer.Add(header)
+ self.box1 = wx.BoxSizer(wx.VERTICAL)
+
+ # We expect to print 4 lines of information, reserve space using blank
+ # lines.
+ self.settings_summary_text = wx.StaticText(self, label = "\n\n\n\n")
+ self.settings_summary_text.Wrap(width - 10)
+
+ self.box1.Add(self.settings_summary_text, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ self.start_button = wx.Button(self, 10, "Start", (20, 20))
+ self.Bind(wx.EVT_BUTTON, self.start_lmc, self.start_button)
+
+ self.start_button.SetToolTipString("Start creating an image, using the"
+ "above settings.")
+
+ self.start_button.SetSize(self.start_button.GetBestSize())
+ self.box1.Add(self.start_button, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+
+ self.status_grid = wx.FlexGridSizer(0, 2, 0, 0)
+
+ self.status_grid.Add(wx.StaticText(self, label="Downloading files"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.downloading_files_status = wx.StaticText(self, label="")
+ self.status_grid.Add(self.downloading_files_status,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.status_grid.Add(wx.StaticText(self, label="Unpacking downloads"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.unpacking_files_status = wx.StaticText(self, label="")
+
+ self.status_grid.Add(self.unpacking_files_status,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.status_grid.Add(wx.StaticText(self, label="Installing packages"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.installing_packages_status = wx.StaticText(self, label="")
+
+ self.status_grid.Add(self.installing_packages_status,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.status_grid.Add(wx.StaticText(self, label="Create file system"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.create_file_system_status = wx.StaticText(self, label="")
+
+ self.status_grid.Add(self.create_file_system_status,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.status_grid.Add(wx.StaticText(self, label="Populate file system"),
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.populate_file_system_status = wx.StaticText(self, label="")
+
+ self.status_grid.Add(self.populate_file_system_status,
+ 0,
+ wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP,
+ 5)
+
+ self.sizer.Add(self.box1, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+ self.sizer.Add(self.status_grid, 0, wx.ALIGN_LEFT | wx.ALL, 5)
+ self.SetSizerAndFit(self.sizer)
+ self.sizer.Fit(self)
+ self.Move((50, 50))
+
+ def on_activate(self):
+ """Called just before the page is displayed to update the text based on
+ the users preferences."""
+
+ # The build is stored in different forms depending on if we are using a
+ # release or snapshot but from here on in it is a common value
+ if self.settings['release_or_snapshot'] == "snapshot":
+ self.settings['build'] = self.settings['snapshot_build']
+ else:
+ self.settings['build'] = self.settings['release_build']
+
+ settings_summary = ("Press start to create an image with the "
+ "following settings:\n")
+ settings_summary += "Operating System: " + self.settings['image'] + "\n"
+ settings_summary += "Hardware: " + self.settings['hardware'] + "\n"
+
+ # Assumption is that a file may be in a long path, we don't know how
+ # big the font is and we don't want to allow the path to run off the
+ # end of the line, so if a file is chosen, just show the file name.
+ # Devices are (probably) /dev/some_short_name and the user really needs
+ # to check them, so we show the whole thing.
+ path = self.settings['path_selected']
+ if self.settings['write_to_file_or_device'] == "file":
+ path = self.settings['path_selected'].split(os.sep)[-1]
+
+ settings_summary += ( "Writing image to "
+ + self.settings['write_to_file_or_device']
+ + " "
+ + path)
+
+ self.settings_summary_text.SetLabel(settings_summary)
+ self.settings_summary_text.Wrap(self.width - 10)
+
+ def start_lmc(self, event):
+ """Start a thread that runs linaro-media-create and a timer, which
+ checks for UI updates every 100ms"""
+
+ if self.settings['write_to_file_or_device'] == "file":
+ self.settings['image_file'] = self.settings['path_selected']
+ elif self.settings['write_to_file_or_device'] == "device":
+ self.settings['mmc'] = self.settings['path_selected']
+ else:
+ assert False, ("self.config.settings['write_to_file_or_device'] "
+ "was an unexpected value"
+ + self.settings['write_to_file_or_device'])
+
+ image_url, hwpack_url = self.db.get_image_and_hwpack_urls(self.settings)
+
+ # Currently the UI is blocked when LMC is running, so grey out the
+ # buttons to indicate to the user that they won't work!
+ self.wizard.FindWindowById(wx.ID_BACKWARD).Disable()
+ self.wizard.FindWindowById(wx.ID_CANCEL).Disable()
+
+ if(image_url and hwpack_url):
+
+ print image_url
+ print hwpack_url
+
+ self.file_handler = FetchImage.FileHandler()
+
+ tools_dir = os.path.dirname(__file__)
+ if tools_dir == '':
+ tools_dir = None
+
+ self.file_handler.create_media(image_url,
+ hwpack_url,
+ self.settings,
+ tools_dir,
+ True,
+ self)
+
+ self.timer = wx.Timer(self)
+ self.Bind(wx.EVT_TIMER, self.timer_ping, self.timer)
+ self.timer.Start(milliseconds=100, oneShot=True)
+
+ self.start_button.Disable()
+ self.event_queue = Queue.Queue()
+ self.file_handler.start_lmc_gui_thread(self.event_queue)
+ else:
+ print >> sys.stderr, ("Unable to find files that match the"
+ "parameters specified")
+
+ def timer_ping(self, event):
+ """During start_lmc a timer is started to poll for events from
+ linaro-media-create every 100ms. This is the function which is called
+ to do that polling."""
+
+ if self.event_queue.empty() == False:
+ event = self.event_queue.get()
+
+ if event[0] == "start":
+ self.event_start(event[1])
+ self.timer.Start(milliseconds=100, oneShot=True)
+
+ elif event[0] == "end":
+ self.event_end(event[1])
+ self.timer.Start(milliseconds=100, oneShot=True)
+
+ elif event == "terminate":
+ # Process complete. Enable next button.
+ self.wizard.FindWindowById(wx.ID_FORWARD).Enable()
+ self.populate_file_system_status.SetLabel("Done")
+
+ else:
+ print >> sys.stderr, "timer_ping: Unhandled event", event
+
+ else:
+ self.timer.Start(milliseconds=100, oneShot=True)
+
+ def unsigned_packages_query(self, package_list):
+ message = ('In order to continue, I need to install some unsigned'
+ 'packages into the image. Is this OK? The packages are:'
+ '\n\n' + package_list)
+
+ dlg = wx.MessageDialog(self,
+ message,
+ 'Install Unsigned Packages Into Image?',
+ wx.YES_NO | wx.NO_DEFAULT)
+
+ choice = dlg.ShowModal()
+ dlg.Destroy()
+
+ return choice == wx.ID_YES
+
+ #--- Event(s) ---
+ def event_start(self, event):
+ if event == "download OS":
+ self.downloading_files_status.SetLabel("Downloading OS")
+ elif event == "download hwpack":
+ self.downloading_files_status.SetLabel("Downloading Hardware Pack")
+ elif event == "unpack":
+ self.unpacking_files_status.SetLabel("Running")
+ elif event == "installing packages":
+ self.installing_packages_status.SetLabel("Running")
+
+ elif re.search('^unverified_packages:', event):
+ # Get rid of event ID and whitespace invariance
+ packages = " ".join(event.split()[1:])
+ install_unsigned_packages = self.unsigned_packages_query(packages)
+
+ if install_unsigned_packages == False:
+ self.file_handler.kill_create_media()
+ sys.exit(1)
+ else:
+ self.file_handler.send_to_create_process("y")
+
+ elif event == "create file system":
+ self.create_file_system_status.SetLabel("Running")
+ elif event == "populate file system":
+ self.populate_file_system_status.SetLabel("Running")
+ else:
+ print "Unhandled start event:", event
+
+ def event_end(self, event):
+ if event == "download OS":
+ self.downloading_files_status.SetLabel("Done (1/2)")
+ elif event == "download hwpack":
+ self.downloading_files_status.SetLabel("Done")
+ elif event == "unpack":
+ self.unpacking_files_status.SetLabel("Done")
+ elif event == "installing packages":
+ self.installing_packages_status.SetLabel("Done")
+ elif event == "create file system":
+ self.create_file_system_status.SetLabel("Done")
+ elif event == "populate file system":
+ self.populate_file_system_status.SetLabel("Done")
+ else:
+ print "Unhhandled end event:", event
+
+ def event_combo_box_release(self, evt):
+ pass
+
+ def event_combo_box_build(self, evt):
+ pass
+ #--- END event(s) ---
+
+
+class TestDriveWizard(wx.wizard.Wizard):
+ def __init__(self, title):
+ wx.wizard.Wizard.__init__(self, None, -1, title, wx.NullBitmap)
+ self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.on_page_changing)
+ self.done_startup = False
+
+ def on_page_changing(self, evt):
+ 'Executed before the page changes.'
+
+ if self.done_startup == False:
+ self.pages['lmc_settings'].box2.Hide(
+ self.pages['lmc_settings'].optional_settings_box,
+ True)
+
+ self.pages['lmc_settings'].box3.Hide(
+ self.pages['lmc_settings'].confirm_mmc_usage_box,
+ True)
+
+ self.done_startup = True
+
+ page = evt.GetPage()
+
+ if evt.GetDirection(): # If going forwards...
+ # Always enable back button if going forwards
+ self.wizard.FindWindowById(wx.ID_BACKWARD).Enable()
+
+ # If going from a select snapshot or select release page, record
+ # which we were on so the back button of the next page works
+ if(self.config.settings['release_or_snapshot'] == "release"):
+ self.pages['select_os'].SetNext(self.pages['select_release'])
+ self.pages['select_release'].SetPrev(self.pages['select_os'])
+
+ self.pages['select_release'].SetNext(self.pages['lmc_settings'])
+ self.pages['lmc_settings'].SetPrev(self.pages['select_release'])
+ else:
+ self.pages['select_os'].SetNext(self.pages['select_snapshot'])
+ self.pages['select_snapshot'].SetPrev(self.pages['select_os'])
+
+ if(page == self.pages['select_os']):
+ self.pages['select_snapshot'].fill_build_combo_box_for_date(
+ self.config.settings['build_date'])
+
+ self.pages['select_snapshot'].SetNext(self.pages['lmc_settings'])
+ self.pages['lmc_settings'].SetPrev(self.pages['select_snapshot'])
+
+ if page == self.pages['hardware_details']:
+ self.pages['select_os'].fill_os_list()
+
+ if page == self.pages['release_or_snapshot']:
+ self.pages['hardware_details'].on_page_changing()
+
+ # If about to move into the release selection, make sure the list
+ # is populated only with releases that are valid with our current
+ # selection
+ if( page == self.pages['select_os']
+ and self.config.settings['release_or_snapshot'] == "release"):
+ self.pages['select_release'].update_release_and_build_boxes()
+
+ if page == self.pages['select_snapshot']:
+ # Execute when exiting page
+ self.pages['select_snapshot'].update_platform()
+
+ if( page == self.pages['select_snapshot']
+ or page == self.pages['select_release']):
+ self.pages['lmc_settings'].on_activate()
+
+ if page == self.pages['lmc_settings']:
+ # Forward stays disabled until LMC has finished running
+ self.wizard.FindWindowById(wx.ID_FORWARD).Disable()
+ self.pages['run_lmc'].on_activate()
+
+ else: # Always enable the forward button if reversing into a page
+ self.wizard.FindWindowById(wx.ID_FORWARD).Enable()
+
+ def go(self, first_page):
+ file_handler = FetchImage.FileHandler()
+ self.config = FetchImage.FetchImageConfig()
+ self.config.settings["force_download"] = False
+ self.config.settings['compatable_hwpacks'] = ['foo']
+
+ # If the settings file and server index need updating, grab them
+ file_handler.update_files_from_server(show_wx_progress = True)
+
+ # Load settings YAML, which defines the parameters we ask for and
+ # acceptable responses from the user
+ self.config.read_config(file_handler.settings_file)
+
+ # Using the config we have, look up URLs to download data from in
+ # the server index
+ db = FetchImage.DB(file_handler.index_file)
+
+ # Create the wizard and the pages
+ self.wizard = wiz.Wizard(self, -1, "Linaro Media Builder")
+
+ self.pages = {}
+ self.pages['release_or_snapshot'] = ReleaseOrSnapshotPage(self.wizard,
+ self.config)
+ self.wizard.FitToPage(self.pages['release_or_snapshot'])
+ (width, height) = self.wizard.GetSize()
+
+ self.pages['hardware_details'] = AboutMyHardwarePage(self.wizard,
+ self.config,
+ db,
+ width)
+
+ self.pages['select_release'] = SelectStableRelease(self.wizard,
+ self.config,
+ db,
+ width)
+
+ self.pages['select_snapshot'] = SelectSnapshot(self.wizard,
+ self.config,
+ db,
+ width)
+
+ self.pages['select_os'] = SelectOS(self.wizard,
+ self.config,
+ db,
+ width)
+
+ self.pages['lmc_settings'] = LMC_settings(self.wizard,
+ self.config,
+ db,
+ width)
+
+ self.pages['run_lmc'] = RunLMC(self.wizard,
+ self.config,
+ db,
+ width)
+
+ self.pages['release_or_snapshot'].SetNext(
+ self.pages['hardware_details'])
+
+ self.pages['hardware_details'].SetPrev(
+ self.pages['release_or_snapshot'])
+
+ self.pages['hardware_details'].SetNext(self.pages['select_os'])
+ self.pages['select_os'].SetPrev(self.pages['hardware_details'])
+ # Select OS goes to select build, which is customised for
+ # releases or snapshots
+ self.pages['lmc_settings'].SetNext(self.pages['run_lmc'])
+ self.pages['run_lmc'].SetPrev(self.pages['lmc_settings'])
+
+ for (name, page) in self.pages.items():
+ self.wizard.GetPageAreaSizer().Add(page)
+
+ self.wizard.RunWizard(self.pages['release_or_snapshot'])
+
+
+def run(start_page = None):
+ """Wrapper around the full wizard. Is encapsulated in its own function to
+ allow a restart to be performed, as described in __main___, easily"""
+ app = wx.PySimpleApp() # Start the application
+ #logging.basicConfig(level=logging.INFO)
+ w = TestDriveWizard('Simple Wizard')
+ return w.go(start_page)
+
+
+class TestURLLookupFunctions(unittest.TestCase):
+
+ def setUp(self):
+ self.file_handler = FetchImage.FileHandler()
+ self.file_handler.update_files_from_server(show_wx_progress = True)
+ self.config = FetchImage.FetchImageConfig()
+ self.config.settings["force_download"] = False
+
+ # Load settings YAML, which defines the parameters we ask for and
+ # acceptable responses from the user
+ self.config.read_config(self.file_handler.settings_file)
+
+ # Using the config we have, look up URLs to download data from in the
+ # server index
+ self.db = FetchImage.DB(self.file_handler.index_file)
+
+ def test_000_url_lookup_snapshot_builds(self):
+ self.settings = self.config.settings
+ self.settings['release_or_snapshot'] = "snapshot"
+
+ #--- Test finding builds near a particular day ---
+ # This functionality is required for further tests, hence forcing the
+ # run order by putting numbers in the function name.
+ today = wx.DateTime()
+ today.SetToCurrent()
+
+ # -- Don't iterate through platforms for snapshot --
+
+ # -- Select hardware --
+ for self.settings['hardware'] in (
+ self.settings['choice']['hardware'].keys()):
+
+ compatable_hwpacks = self.settings['choice']['hwpack'][
+ self.settings['hardware']]
+
+ future_date, past_date = self.db.get_next_prev_day_with_builds(
+ "linaro-alip",
+ today.FormatISODate().encode('ascii'),
+ compatable_hwpacks)
+
+ if past_date == None:
+ # Some hardware packs are not available in the snapshot repo,
+ # so just skip if they aren't
+ continue
+
+ builds = self.db.get_binary_builds_on_day_from_db(
+ "linaro-alip",
+ past_date,
+ compatable_hwpacks)
+
+ self.assertTrue(len(builds))
+ # If the above assert fails, either the DB is empty, or
+ # db.get_binary_builds_on_day_from_db failed
+
+ def test_100_url_lookup_snapshots(self):
+ self.settings = self.config.settings
+ self.settings['release_or_snapshot'] = "snapshot"
+
+ #--- Test snapshot build lookup ---
+ # -- Fix a build date --
+ # We only need to look up a single snapshot date. Start with today and
+ # go with the day in the DB, build 0
+ today = wx.DateTime()
+ today.SetToCurrent()
+
+ # -- Don't iterate through platforms for snapshot --
+
+ # -- Select hardware --
+ for self.settings['hardware'] in (
+ self.settings['choice']['hardware'].keys()):
+
+ compatable_hwpacks = self.settings['choice']['hwpack'][
+ self.settings['hardware']]
+
+ future_date, past_date = self.db.get_next_prev_day_with_builds(
+ "linaro-alip",
+ today.FormatISODate().encode('ascii'),
+ compatable_hwpacks)
+
+ if past_date == None:
+ # Some hardware packs are not available in the snapshot repo,
+ # so just skip if they aren't
+ continue
+
+ builds = self.db.get_binary_builds_on_day_from_db(
+ "linaro-alip",
+ past_date,
+ compatable_hwpacks)
+
+ self.assertTrue(len(builds))
+ # The above code is tested in test_000_url_lookup_snapshot_build.
+ # If the above assert fails, either the DB is empty, or
+ # db.get_binary_builds_on_day_from_db failed.
+
+ small_date = re.sub('-', '', past_date)
+ self.settings['build'] = small_date + ":" + "0"
+
+ # -- Iterate through hardware packs --
+ for self.settings['hwpack'] in compatable_hwpacks:
+
+ # If hardware pack is available...
+ if(self.settings['hwpack']
+ in self.db.get_hwpacks('snapshot_hwpacks')):
+
+ # -- Iterate through images
+ os_list = self.db.get_os_list_from('snapshot_binaries')
+
+ for self.settings['image'] in os_list:
+ if re.search('old', self.settings['image']):
+ # Directories with old in the name are of no
+ # interest to us
+ continue
+
+ # -- Check build which matches these parameters
+ # (builds that don't match are excluded in UI) --
+ if( len(self.db.execute_return_list(
+ 'select * from snapshot_hwpacks '
+ 'where hardware == ? '
+ 'and date == ? '
+ 'and build == ?',
+ (self.settings['hwpack'],
+ small_date,
+ "0")))
+ and len(self.db.execute_return_list(
+ 'select * from snapshot_binaries '
+ 'where image == ? '
+ 'and date == ? '
+ 'and build == ?',
+ (self.settings['image'],
+ small_date,
+ "0")))):
+
+ # - Run the function under test! -
+ image_url, hwpack_url = (
+ self.db.get_image_and_hwpack_urls(self.settings))
+
+ self.assertTrue(image_url)
+ self.assertTrue(hwpack_url)
+
+ def test_101_url_lookup_releases(self):
+ #--- Test release build lookup ---
+ self.settings = self.config.settings
+ self.settings['release_or_snapshot'] = "release"
+ # -- Select hardware --
+ for self.settings['hardware'] in (
+ self.settings['choice']['hardware'].keys()):
+ compatable_hwpacks = (
+ self.settings['choice']['hwpack'][self.settings['hardware']])
+
+ # -- Iterate through hardware packs --
+ for self.settings['hwpack'] in compatable_hwpacks:
+
+ # If hardware pack is available...
+ if(self.settings['hwpack']
+ in self.db.get_hwpacks('release_hwpacks')):
+
+ # -- Iterate through images
+ os_list = self.db.get_os_list_from('release_binaries')
+
+ for self.settings['image'] in os_list:
+ if re.search('old', self.settings['image']):
+ # Directories with old in the name are of no
+ # interest to us
+ continue
+
+ for platform, ignore in (
+ self.settings['choice']['platform'].items()):
+ self.settings['platform'] = platform
+
+ # -- Iterate through available builds --
+ builds = self.db.get_builds(
+ self.settings['platform'],
+ self.settings['image'])
+
+ for build in builds:
+ self.settings['build'] = build
+
+ # -- Check build which matches these parameters
+ #(builds that don't match are excluded in UI)--
+ if( len(self.db.execute_return_list(
+ 'select * from release_hwpacks '
+ 'where platform == ? '
+ 'and hardware == ? '
+ 'and build == ?',
+ (self.settings['platform'],
+ self.settings['hwpack'],
+ self.settings['build'])))
+ and len(self.db.execute_return_list(
+ 'select * from release_binaries '
+ 'where platform == ? '
+ 'and image == ? '
+ 'and build == ?',
+ (self.settings['platform'],
+ self.settings['image'],
+ self.settings['build'])))):
+
+ # - Run the function under test! -
+ image_url, hwpack_url = (
+ self.db.get_image_and_hwpack_urls(self.settings))
+ self.assertTrue(image_url)
+ self.assertTrue(hwpack_url)
+
+if __name__ == '__main__':
+ run()
=== modified file 'linaro-android-media-create'
@@ -133,12 +133,13 @@
unpack_android_binary_tarball(args.system, SYSTEM_DIR)
unpack_android_binary_tarball(args.userdata, DATA_DIR)
- # Create partitions
+ # Create partitions
boot_partition, system_partition, cache_partition, \
data_partition, sdcard_partition = setup_android_partitions( \
board_config, media, args.image_size, args.boot_label,
args.should_create_partitions, args.should_align_boot_part)
+ board_config.populate_raw_partition(args.device, BOOT_DIR)
populate_partition(BOOT_DIR + "/boot", BOOT_DISK, boot_partition)
board_config.populate_boot_script(boot_partition, BOOT_DISK, args.consoles)
populate_partition(SYSTEM_DIR + "/system", SYSTEM_DISK, system_partition)
=== modified file 'linaro-hwpack-install'
@@ -36,7 +36,7 @@
FORCE_YES="no"
SOURCES_LIST_FILE="${TEMP_DIR}/sources.list"
APT_GET_OPTIONS="Dir::Etc::SourceList=${SOURCES_LIST_FILE}"
-SUPPORTED_FORMATS="1.0" # A space-separated list of hwpack formats.
+SUPPORTED_FORMATS="1.0 2.0" # A space-separated list of hwpack formats.
sudo="sudo"
if [ $(id -u) -eq 0 ]; then
=== modified file 'linaro-media-create'
@@ -22,7 +22,6 @@
import os
import sys
import tempfile
-import subprocess
from linaro_image_tools import cmd_runner
@@ -43,7 +42,11 @@
unpack_binary_tarball,
)
from linaro_image_tools.media_create import get_args_parser
-from linaro_image_tools.utils import ensure_command, is_arm_host
+from linaro_image_tools.utils import (
+ ensure_command,
+ is_arm_host,
+ verify_file_integrity,
+ )
# Just define the global variables
TMP_DIR = None
@@ -101,19 +104,14 @@
ROOT_DISK = os.path.join(TMP_DIR, 'root-disc')
board_config = board_configs[args.board]
+ board_config.set_metadata(args.hwpacks)
ensure_required_commands(args)
sig_file_list = args.hwpacksigs[:]
- verified_files = []
if args.binarysig is not None:
sig_file_list.append(args.binarysig)
- for sig_file in sig_file_list:
- hash_file = sig_file[0:-len('.asc')]
- cmd_runner.run(['gpg', '--verify', sig_file]).wait()
- sha1sums_out, _ = cmd_runner.run(['sha1sum', '-c', hash_file],
- stdout=subprocess.PIPE).communicate()
- verified_files.extend(sha1sums_out.replace(': OK', '').splitlines())
+ verified_files = verify_file_integrity(sig_file_list)
for verified_file in verified_files:
print 'Hash verification of file %s OK.' % verified_file
=== modified file 'linaro_image_tools/FetchImage.py'
@@ -420,8 +420,8 @@
def update_files_from_server(self, force_download=False,
show_wx_progress=False):
- settings_url = "http://z.nanosheep.org/fetch_image_settings.yaml"
- server_index_url = "http://z.nanosheep.org/server_index.bz2"
+ settings_url = "http://releases.linaro.org/fetch_image/fetch_image_settings.yaml"
+ server_index_url = "http://releases.linaro.org/fetch_image/server_index.bz2"
self.settings_file = self.download_if_old(settings_url,
force_download,
=== modified file 'linaro_image_tools/cmd_runner.py'
@@ -36,7 +36,7 @@
def run(args, as_root=False, chroot=None, stdin=None, stdout=None,
- stderr=None):
+ stderr=None, cwd=None):
"""Run the given command as a sub process.
Return a Popen instance.
@@ -60,7 +60,7 @@
as_root = True
if as_root and os.getuid() != 0:
args = SUDO_ARGS + args
- return Popen(args, stdin=stdin, stdout=stdout, stderr=stderr)
+ return Popen(args, stdin=stdin, stdout=stdout, stderr=stderr, cwd=cwd)
class Popen(subprocess.Popen):
=== modified file 'linaro_image_tools/hwpack/builder.py'
@@ -21,6 +21,12 @@
import logging
import errno
+import subprocess
+import tempfile
+import os
+import shutil
+
+from linaro_image_tools import cmd_runner
from linaro_image_tools.hwpack.config import Config
from linaro_image_tools.hwpack.hardwarepack import HardwarePack, Metadata
@@ -45,6 +51,33 @@
"No such config file: '%s'" % self.filename)
+class PackageUnpacker(object):
+ def __enter__(self):
+ self.tempdir = tempfile.mkdtemp()
+ return self
+
+ def __exit__(self, type, value, traceback):
+ if self.tempdir is not None and os.path.exists(self.tempdir):
+ shutil.rmtree(self.tempdir)
+
+ def unpack_package(self, package_file_name):
+ # We could extract only a single file, but since dpkg will pipe
+ # the entire package through tar anyway we might as well extract all.
+ p = cmd_runner.run(["tar", "-C", self.tempdir, "-xf", "-"],
+ stdin=subprocess.PIPE)
+ cmd_runner.run(["dpkg", "--fsys-tarfile", package_file_name],
+ stdout=p.stdin).communicate()
+ p.communicate()
+
+ def get_file(self, package, file):
+ self.unpack_package(package)
+ logger.debug("Unpacked package %s." % package)
+ temp_file = os.path.join(self.tempdir, file)
+ assert os.path.exists(temp_file), "The file '%s' was " \
+ "not found in the package '%s'." % (file, package)
+ return temp_file
+
+
class HardwarePackBuilder(object):
def __init__(self, config_path, version, local_debs):
@@ -56,9 +89,27 @@
raise ConfigFileMissing(config_path)
raise
self.config.validate()
+ self.format = self.config.format
self.version = version
self.local_debs = local_debs
+ def find_fetched_package(self, packages, wanted_package_name):
+ wanted_package = None
+ for package in packages:
+ if package.name == wanted_package_name:
+ wanted_package = package
+ break
+ else:
+ raise AssertionError("Package '%s' was not fetched." % \
+ wanted_package_name)
+ packages.remove(wanted_package)
+ return wanted_package
+
+ def add_file_to_hwpack(self, package, wanted_file, package_unpacker, hwpack, target_path):
+ tempfile_name = package_unpacker.get_file(
+ package.filepath, wanted_file)
+ return hwpack.add_file(target_path, tempfile_name)
+
def build(self):
for architecture in self.config.architectures:
logger.info("Building for %s" % architecture)
@@ -70,6 +121,8 @@
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)
local_packages = [
FetchedPackage.from_deb(deb)
for deb in self.local_debs]
@@ -81,10 +134,18 @@
fetcher = PackageFetcher(
sources, architecture=architecture,
prefer_label=LOCAL_ARCHIVE_LABEL)
- with fetcher:
+ with fetcher, PackageUnpacker() as package_unpacker:
fetcher.ignore_packages(self.config.assume_installed)
packages = fetcher.fetch_packages(
packages, download_content=self.config.include_debs)
+
+ if self.config.u_boot_package is not None:
+ u_boot_package = self.find_fetched_package(
+ packages, self.config.u_boot_package)
+ hwpack.metadata.u_boot = self.add_file_to_hwpack(
+ u_boot_package, self.config.u_boot_file,
+ package_unpacker, hwpack, hwpack.U_BOOT_DIR)
+
logger.debug("Adding packages to hwpack")
hwpack.add_packages(packages)
for local_package in local_packages:
=== modified file 'linaro_image_tools/hwpack/config.py'
@@ -22,6 +22,10 @@
import ConfigParser
import re
+from linaro_image_tools.hwpack.hardwarepack_format import (
+ HardwarePackFormatV1,
+ HardwarePackFormatV2,
+ )
class HwpackConfigError(Exception):
pass
@@ -38,10 +42,32 @@
SOURCES_ENTRY_KEY = "sources-entry"
PACKAGES_KEY = "packages"
PACKAGE_REGEX = NAME_REGEX
+ PATH_REGEX = r"[a-z0-9][a-z0-9+\-./_]+$"
ORIGIN_KEY = "origin"
MAINTAINER_KEY = "maintainer"
ARCHITECTURES_KEY = "architectures"
ASSUME_INSTALLED_KEY = "assume-installed"
+ U_BOOT_PACKAGE_KEY = "u-boot-package"
+ U_BOOT_FILE_KEY = "u-boot-file"
+ SERIAL_TTY_KEY = "serial_tty"
+ KERNEL_ADDR_KEY = "kernel_addr"
+ INITRD_ADDR_KEY = "initrd_addr"
+ LOAD_ADDR_KEY = "load_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"
+
+ DEFINED_PARTITION_LAYOUTS = [
+ 'bootfs16_rootfs',
+ 'bootfs_rootfs',
+ #'reserved_bootfs_rootfs',
+ ]
+
def __init__(self, fp):
"""Create a Config.
@@ -58,15 +84,52 @@
"""
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()
self._validate_support()
self._validate_packages()
self._validate_architectures()
self._validate_assume_installed()
+
+ if self.format.has_v2_fields:
+ self._validate_u_boot_package()
+ self._validate_u_boot_file()
+ self._validate_serial_tty()
+ self._validate_kernel_addr()
+ self._validate_initrd_addr()
+ self._validate_load_addr()
+ self._validate_wired_interfaces()
+ self._validate_wireless_interfaces()
+ self._validate_partition_layout()
+ self._validate_mmc_id()
+ self._validate_boot_min_size()
+ self._validate_root_min_size()
+ self._validate_loader_min_size()
+
self._validate_sections()
@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 format_string == '1.0':
+ return HardwarePackFormatV1()
+ elif format_string == '2.0':
+ return HardwarePackFormatV2()
+ else:
+ 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)
@@ -101,6 +164,96 @@
return None
@property
+ def serial_tty(self):
+ """/dev device name of the serial console for this kernel
+
+ A str.
+ """
+ return self._get_option_from_main_section(self.SERIAL_TTY_KEY)
+
+ @property
+ def kernel_addr(self):
+ """address where u-boot should load the kernel
+
+ An int.
+ """
+ return self._get_option_from_main_section(self.KERNEL_ADDR_KEY)
+
+ @property
+ def initrd_addr(self):
+ """address where u-boot should load the kernel
+
+ An int.
+ """
+ return self._get_option_from_main_section(self.INITRD_ADDR_KEY)
+
+ @property
+ def load_addr(self):
+ """address for uImage generation
+
+ An int.
+ """
+ return self._get_option_from_main_section(self.LOAD_ADDR_KEY)
+
+ @property
+ def wired_interfaces(self):
+ """The interfaces for wired networks
+
+ A list of str.
+ """
+ return self._get_list_from_main_section(self.WIRED_INTERFACES_KEY)
+
+ @property
+ def wireless_interfaces(self):
+ """The interfaces for wireless networks
+
+ A list of str.
+ """
+ return self._get_list_from_main_section(self.WIRELESS_INTERFACES_KEY)
+
+ @property
+ def partition_layout(self):
+ """bootfs16_rootfs, bootfs_rootfs and reserved_bootfs_rootfs;
+ controls what kind of SD card partition layout we should use when
+ writing images
+
+ A str.
+ """
+ return self._get_option_from_main_section(self.PARTITION_LAYOUT_KEY)
+
+ @property
+ def mmc_id(self):
+ """which MMC drive contains the boot filesystem
+
+ An int.
+ """
+ return self._get_option_from_main_section(self.MMC_ID_KEY)
+
+ @property
+ def root_min_size(self):
+ """Minimum size of the root partition, in MiB.
+
+ An int.
+ """
+ return self._get_option_from_main_section(self.ROOT_MIN_SIZE_KEY)
+
+ @property
+ def boot_min_size(self):
+ """Minimum size of the boot partition, in MiB.
+
+ An int.
+ """
+ return self._get_option_from_main_section(self.BOOT_MIN_SIZE_KEY)
+
+ @property
+ def loader_min_size(self):
+ """Minimum size of the optional loader partition, in MiB.
+
+ An int.
+ """
+ return self._get_option_from_main_section(self.LOADER_MIN_SIZE_KEY)
+
+ @property
def origin(self):
"""The origin that should be recorded in the hwpack.
@@ -144,6 +297,22 @@
return self._get_list_from_main_section(self.PACKAGES_KEY)
@property
+ def u_boot_package(self):
+ """The u-boot package that contains the u-boot bin.
+
+ A str.
+ """
+ return self._get_option_from_main_section(self.U_BOOT_PACKAGE_KEY)
+
+ @property
+ def u_boot_file(self):
+ """The u-boot bin file that will be unpacked from the u-boot package.
+
+ A str.
+ """
+ return self._get_option_from_main_section(self.U_BOOT_FILE_KEY)
+
+ @property
def architectures(self):
"""The architectures to build the hwpack for.
@@ -174,17 +343,121 @@
section_name, self.SOURCES_ENTRY_KEY)
return sources
+ def _validate_format(self):
+ format = self.format
+ if not format:
+ raise HwpackConfigError("Empty value for format")
+ if not format.is_supported:
+ raise HwpackConfigError("Format version '%s' is not supported." % \
+ format)
+
+ def _assert_matches_pattern(self, regex, config_item, error_message):
+ if re.match(regex, config_item) is None:
+ raise HwpackConfigError(error_message)
+
def _validate_name(self):
try:
name = self.name
if not name:
raise HwpackConfigError("Empty value for name")
- if re.match(self.NAME_REGEX, name) is None:
- raise HwpackConfigError("Invalid name: %s" % name)
+ self._assert_matches_pattern(
+ self.NAME_REGEX, name, "Invalid name: %s" % name)
except ConfigParser.NoOptionError:
raise HwpackConfigError(
"No name in the [%s] section" % self.MAIN_SECTION)
+ def _validate_u_boot_file(self):
+ u_boot_file = self.u_boot_file
+ if not u_boot_file:
+ raise HwpackConfigError("No u_boot_file in the [%s] section" % \
+ self.MAIN_SECTION)
+ self._assert_matches_pattern(
+ self.PATH_REGEX, u_boot_file, "Invalid path: %s" % u_boot_file)
+
+ def _validate_serial_tty(self):
+ serial_tty = self.serial_tty
+ if serial_tty is None:
+ return
+ 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_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)
+
+ 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)
+
+ 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)
+
+ def _validate_wired_interfaces(self):
+ pass
+
+ def _validate_wireless_interfaces(self):
+ 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)))
+
+ def _validate_mmc_id(self):
+ mmc_id = self.mmc_id
+ if mmc_id is None:
+ return
+ try:
+ int(mmc_id)
+ except:
+ raise HwpackConfigError("Invalid mmc id %s" % (mmc_id))
+
+ def _validate_root_min_size(self):
+ root_min_size = self.root_min_size
+ if root_min_size is None:
+ return
+ try:
+ assert int(root_min_size) > 0
+ except:
+ raise HwpackConfigError(
+ "Invalid root min size %s" % (root_min_size))
+
+ def _validate_boot_min_size(self):
+ boot_min_size = self.boot_min_size
+ if boot_min_size is None:
+ return
+ try:
+ assert int(boot_min_size) > 0
+ except:
+ raise HwpackConfigError(
+ "Invalid boot min size %s" % (boot_min_size))
+
+ def _validate_loader_min_size(self):
+ loader_min_size = self.loader_min_size
+ if loader_min_size is None:
+ return
+ try:
+ assert int(loader_min_size) > 0
+ except:
+ raise HwpackConfigError(
+ "Invalid loader min size %s" % (loader_min_size))
+
def _validate_include_debs(self):
try:
self.include_debs
@@ -206,10 +479,21 @@
"No %s in the [%s] section"
% (self.PACKAGES_KEY, self.MAIN_SECTION))
for package in packages:
- if re.match(self.PACKAGE_REGEX, package) is None:
- raise HwpackConfigError(
- "Invalid value in %s in the [%s] section: %s"
- % (self.PACKAGES_KEY, self.MAIN_SECTION, package))
+ self._assert_matches_pattern(
+ self.PACKAGE_REGEX, package, "Invalid value in %s in the " \
+ "[%s] section: %s" % (self.PACKAGES_KEY, self.MAIN_SECTION,
+ package))
+
+ def _validate_u_boot_package(self):
+ u_boot_package = self.u_boot_package
+ if not u_boot_package:
+ raise HwpackConfigError(
+ "No %s in the [%s] section"
+ % (self.U_BOOT_PACKAGE_KEY, self.MAIN_SECTION))
+ 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))
def _validate_architectures(self):
architectures = self.architectures
@@ -221,11 +505,10 @@
def _validate_assume_installed(self):
assume_installed = self.assume_installed
for package in assume_installed:
- if re.match(self.PACKAGE_REGEX, package) is None:
- raise HwpackConfigError(
- "Invalid value in %s in the [%s] section: %s"
- % (self.ASSUME_INSTALLED_KEY, self.MAIN_SECTION,
- package))
+ 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:
=== modified file 'linaro_image_tools/hwpack/hardwarepack.py'
@@ -20,6 +20,7 @@
# USA.
import time
+import os
from linaro_image_tools.hwpack.better_tarfile import writeable_tarfile
from linaro_image_tools.hwpack.packages import (
@@ -27,6 +28,9 @@
get_packages_file,
PackageMaker,
)
+from linaro_image_tools.hwpack.hardwarepack_format import (
+ HardwarePackFormatV1,
+)
class Metadata(object):
@@ -55,11 +59,12 @@
"""
def __init__(self, name, version, architecture, origin=None,
- maintainer=None, support=None):
+ maintainer=None, support=None, format=HardwarePackFormatV1()):
"""Create the Metadata for a hardware pack.
See the instance variables for a description of the arguments.
"""
+ self.format = format
self.name = name
if ' ' in version:
raise AssertionError(
@@ -72,6 +77,29 @@
self.architecture = architecture
@classmethod
+ def add_v2_config(self, serial_tty=None, kernel_addr=None, initrd_addr=None,
+ load_addr=None, fdt=None, wired_interfaces=[],
+ wireless_interfaces=[], partition_layout=None,
+ mmc_id=None, boot_min_size=None, root_min_size=None,
+ loader_min_size=None):
+ """Add fields that are specific to the new format.
+
+ These fields are not present in earlier config files.
+ """
+ self.u_boot = None
+ self.serial_tty = serial_tty
+ self.kernel_addr = kernel_addr
+ self.initrd_addr = initrd_addr
+ self.load_addr = load_addr
+ self.wired_interfaces = wired_interfaces
+ self.wireless_interfaces = wireless_interfaces
+ self.partition_layout = partition_layout
+ self.mmc_id = mmc_id
+ self.boot_min_size = boot_min_size
+ self.root_min_size = root_min_size
+ self.loader_min_size = loader_min_size
+
+ @classmethod
def from_config(cls, config, version, architecture):
"""Create a Metadata from a Config object.
@@ -89,9 +117,24 @@
targetting.
:type architecture: str
"""
- return cls(
+ metadata = cls(
config.name, version, architecture, origin=config.origin,
- maintainer=config.maintainer, support=config.support)
+ maintainer=config.maintainer, support=config.support,
+ format=config.format)
+
+ if config.format.has_v2_fields:
+ metadata.add_v2_config(serial_tty=config.serial_tty,
+ 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,
+ 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)
+ return metadata
def __str__(self):
"""Get the contents of the metadata file."""
@@ -104,6 +147,36 @@
metadata += "MAINTAINER=%s\n" % self.maintainer
if self.support is not None:
metadata += "SUPPORT=%s\n" % self.support
+
+ if not self.format.has_v2_fields:
+ return metadata
+
+ if self.u_boot is not None:
+ metadata += "U_BOOT=%s\n" % self.u_boot
+ if self.serial_tty is not None:
+ metadata += "SERIAL_TTY=%s\n" % self.serial_tty
+ if self.kernel_addr is not None:
+ metadata += "KERNEL_ADDR=%s\n" % self.kernel_addr
+ if self.initrd_addr is not None:
+ metadata += "INITRD_ADDR=%s\n" % self.initrd_addr
+ if self.load_addr is not None:
+ metadata += "LOAD_ADDR=%s\n" % self.load_addr
+ if self.wired_interfaces != []:
+ metadata += "WIRED_INTERFACES=%s\n" % " ".join(self.wired_interfaces)
+ if self.wireless_interfaces != []:
+ metadata += "WIRELESS_INTERFACES=%s\n" % " ".join(
+ self.wireless_interfaces)
+ if self.partition_layout is not None:
+ metadata += "PARTITION_LAYOUT=%s\n" % self.partition_layout
+ if self.mmc_id is not None:
+ metadata += "MMC_ID=%s\n" % self.mmc_id
+ if self.boot_min_size is not None:
+ metadata += "BOOT_MIN_SIZE=%s\n" % self.boot_min_size
+ if self.root_min_size is not None:
+ metadata += "ROOT_MIN_SIZE=%s\n" % self.root_min_size
+ if self.loader_min_size is not None:
+ metadata += "LOADER_MIN_SIZE=%s\n" % self.loader_min_size
+
return metadata
@@ -116,8 +189,6 @@
:type FORMAT: str
"""
- # The format version cannot contain white spaces.
- FORMAT = "1.0"
FORMAT_FILENAME = "FORMAT"
METADATA_FILENAME = "metadata"
MANIFEST_FILENAME = "manifest"
@@ -125,6 +196,7 @@
PACKAGES_FILENAME = "%s/Packages" % PACKAGES_DIRNAME
SOURCES_LIST_DIRNAME = "sources.list.d"
SOURCES_LIST_GPG_DIRNAME = "sources.list.d.gpg"
+ U_BOOT_DIR = "u-boot"
def __init__(self, metadata):
"""Create a HardwarePack.
@@ -135,6 +207,8 @@
self.metadata = metadata
self.sources = {}
self.packages = []
+ self.format = metadata.format
+ self.files = []
def filename(self, extension=".tar.gz"):
"""The filename that this hardware pack should have.
@@ -200,6 +274,11 @@
relationships, self.metadata.architecture)
self.packages.append(FetchedPackage.from_deb(deb_file_path))
+ def add_file(self, dir, file):
+ target_file = os.path.join(dir, os.path.basename(file))
+ self.files.append((file, target_file))
+ return target_file
+
def manifest_text(self):
manifest_content = ""
for package in self.packages:
@@ -225,9 +304,11 @@
kwargs["default_mtime"] = time.time()
with writeable_tarfile(fileobj, mode="w:gz", **kwargs) as tf:
tf.create_file_from_string(
- self.FORMAT_FILENAME, self.FORMAT + "\n")
+ self.FORMAT_FILENAME, "%s\n" % self.format)
tf.create_file_from_string(
self.METADATA_FILENAME, str(self.metadata))
+ for fs_file_name, arc_file_name in self.files:
+ tf.add(fs_file_name, arcname=arc_file_name)
tf.create_dir(self.PACKAGES_DIRNAME)
for package in self.packages:
if package.content is not None:
=== added file 'linaro_image_tools/hwpack/hardwarepack_format.py'
@@ -0,0 +1,59 @@
+# Copyright (C) 2010, 2011 Linaro
+#
+# Author: James Westby <james.westby@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 logging
+
+
+logger = logging.getLogger(__name__)
+
+
+class HardwarePackFormat(object):
+ def __init__(self):
+ self.format_as_string = None
+ self.is_deprecated = False
+ self.is_supported = False
+ self.has_v2_fields = False
+
+ def __str__(self):
+ if self.format_as_string is None:
+ raise NotImplementedError()
+ if self.is_deprecated:
+ logger.warning("The format '%s' is deprecated, please update " \
+ "your hardware pack configuration." % \
+ self.format_as_string)
+ return self.format_as_string
+
+
+class HardwarePackFormatV1(HardwarePackFormat):
+ def __init__(self):
+ super(HardwarePackFormatV1, self).__init__()
+ self.format_as_string = "1.0"
+ self.is_supported = True
+ self.is_deprecated = False
+
+
+class HardwarePackFormatV2(HardwarePackFormat):
+ def __init__(self):
+ super(HardwarePackFormatV2, self).__init__()
+ self.format_as_string = "2.0"
+ self.is_supported = True
+ self.is_deprecated = False
+ self.has_v2_fields = True
=== modified file 'linaro_image_tools/hwpack/packages.py'
@@ -724,4 +724,5 @@
"The item %r could not be fetched: %s" %
(acqfile.destfile, acqfile.error_text))
result_package.content = open(destfile)
+ result_package._file_path = destfile
return fetched.values()
=== modified file 'linaro_image_tools/hwpack/tests/test_builder.py'
@@ -27,6 +27,7 @@
from linaro_image_tools.hwpack.builder import (
ConfigFileMissing,
+ PackageUnpacker,
HardwarePackBuilder,
logger as builder_logger,
)
@@ -50,6 +51,10 @@
Not,
)
from linaro_image_tools.testing import TestCaseWithFixtures
+from linaro_image_tools.tests.fixtures import (
+ MockSomethingFixture,
+ MockCmdRunnerPopenFixture,
+ )
class ConfigFileMissingTests(TestCase):
@@ -59,6 +64,52 @@
self.assertEqual("No such config file: 'path'", str(exc))
+class PackageUnpackerTests(TestCaseWithFixtures):
+
+ def test_creates_tempdir(self):
+ with PackageUnpacker() as package_unpacker:
+ self.assertTrue(os.path.exists(package_unpacker.tempdir))
+
+ def test_tempfiles_are_removed(self):
+ tempdir = None
+ with PackageUnpacker() as package_unpacker:
+ tempdir = package_unpacker.tempdir
+ self.assertFalse(os.path.exists(tempdir))
+
+ def test_unpack_package(self):
+ fixture = MockCmdRunnerPopenFixture(assert_child_finished=False)
+ self.useFixture(fixture)
+ package_file_name = "package-to-unpack"
+ with PackageUnpacker() as package_unpacker:
+ package_unpacker.unpack_package(package_file_name)
+ package_dir = package_unpacker.tempdir
+ self.assertEquals(
+ ["tar -C %s -xf -" % package_dir,
+ "dpkg --fsys-tarfile %s" % package_file_name],
+ fixture.mock.commands_executed)
+
+ def test_get_file_returns_tempfile(self):
+ package = 'package'
+ file = 'dummyfile'
+ with PackageUnpacker() as package_unpacker:
+ self.useFixture(MockSomethingFixture(
+ package_unpacker, 'unpack_package', lambda package: None))
+ self.useFixture(MockSomethingFixture(
+ os.path, 'exists', lambda file: True))
+ tempfile = package_unpacker.get_file(package, file)
+ self.assertEquals(tempfile,
+ os.path.join(package_unpacker.tempdir, file))
+
+ def test_get_file_raises(self):
+ package = 'package'
+ file = 'dummyfile'
+ with PackageUnpacker() as package_unpacker:
+ self.useFixture(MockSomethingFixture(
+ package_unpacker, 'unpack_package', lambda package: None))
+ self.assertRaises(AssertionError, package_unpacker.get_file,
+ package, file)
+
+
class HardwarePackBuilderTests(TestCaseWithFixtures):
def setUp(self):
@@ -95,6 +146,58 @@
config = self.useFixture(ConfigFileFixture(config_text))
return Metadata(hwpack_name, hwpack_version, architecture), config
+ def test_find_fetched_package_finds(self):
+ package_name = "dummy-package"
+ wanted_package_name = "wanted-package"
+ available_package = DummyFetchedPackage(package_name, "1.1")
+ wanted_package = DummyFetchedPackage(wanted_package_name, "1.1")
+
+ sources_dict = self.sourcesDictForPackages([available_package,
+ wanted_package])
+ _, config = self.makeMetaDataAndConfigFixture(
+ [package_name, wanted_package_name], sources_dict,
+ extra_config={'format': '2.0', 'u-boot-package': 'wanted-package',
+ 'u-boot-file': 'wanted-file',
+ 'partition_layout': 'bootfs_rootfs'})
+ builder = HardwarePackBuilder(config.filename, "1.0", [])
+ found_package = builder.find_fetched_package(
+ [available_package, wanted_package], wanted_package_name)
+ self.assertEquals(wanted_package, found_package)
+
+ def test_find_fetched_package_removes(self):
+ package_name = "dummy-package"
+ wanted_package_name = "wanted-package"
+ available_package = DummyFetchedPackage(package_name, "1.1")
+ wanted_package = DummyFetchedPackage(wanted_package_name, "1.1")
+
+ sources_dict = self.sourcesDictForPackages([available_package,
+ wanted_package])
+ _, config = self.makeMetaDataAndConfigFixture(
+ [package_name, wanted_package_name], sources_dict,
+ extra_config={'format': '2.0', 'u-boot-package': 'wanted-package',
+ 'u-boot-file': 'wanted-file',
+ 'partition_layout': 'bootfs_rootfs'})
+ builder = HardwarePackBuilder(config.filename, "1.0", [])
+ packages = [available_package, wanted_package]
+ builder.find_fetched_package(packages, wanted_package_name)
+ self.assertEquals(packages, [available_package])
+
+ def test_find_fetched_package_raises(self):
+ package_name = "dummy-package"
+ wanted_package_name = "wanted-package"
+ available_package = DummyFetchedPackage(package_name, "1.1")
+
+ sources_dict = self.sourcesDictForPackages([available_package])
+ _, config = self.makeMetaDataAndConfigFixture(
+ [package_name], sources_dict,
+ extra_config={'format': '2.0', 'u-boot-package': 'wanted-package',
+ 'u-boot-file': 'wanted-file',
+ 'partition_layout': 'bootfs_rootfs'})
+ builder = HardwarePackBuilder(config.filename, "1.0", [])
+ packages = [available_package]
+ self.assertRaises(AssertionError, builder.find_fetched_package,
+ packages, wanted_package_name)
+
def test_creates_external_manifest(self):
available_package = DummyFetchedPackage("foo", "1.1")
sources_dict = self.sourcesDictForPackages([available_package])
=== modified file 'linaro_image_tools/hwpack/tests/test_config.py'
@@ -30,6 +30,13 @@
valid_start = (
"[hwpack]\nname = ahwpack\npackages = foo\narchitectures = armel\n")
+ valid_start_v2 = valid_start + "format = 2.0\n"
+ valid_complete_v2 = (valid_start_v2 +
+ "u-boot-package = u-boot-linaro-s5pv310\n" \
+ "u-boot-file = usr/lib/u-boot/smdkv310/" \
+ "u-boot.bin\nserial_tty=ttySAC1\n" \
+ "partition_layout = bootfs_rootfs\n")
+ valid_end = "[ubuntu]\nsources-entry = foo bar\n"
def test_create(self):
config = Config(StringIO())
@@ -170,6 +177,231 @@
"[ubuntu]\nsources-entry = foo bar\n")
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)
+
+ def test_validate_invalid_u_boot_package_name(self):
+ config = self.get_config(
+ self.valid_start_v2 + "u-boot-package = ~~\n")
+ self.assertValidationError(
+ "Invalid value in u-boot-package in the [hwpack] section: ~~",
+ config)
+
+ def test_validate_empty_u_boot_package(self):
+ config = self.get_config(
+ self.valid_start_v2 + "u-boot-package = \n")
+ self.assertValidationError(
+ "No u-boot-package in the [hwpack] section", config)
+
+ def test_validate_no_u_boot_file(self):
+ config = self.get_config(self.valid_start_v2 +
+ "u-boot-package = u-boot-linaro-s5pv310\n")
+ self.assertValidationError("No u_boot_file in the [hwpack] section",
+ config)
+
+ def test_validate_empty_u_boot_file(self):
+ config = self.get_config(self.valid_start_v2 +
+ "u-boot-package = u-boot-linaro-s5pv310\n" \
+ "u-boot-file = \n")
+ self.assertValidationError("No u_boot_file in the [hwpack] section", config)
+
+ def test_validate_invalid_u_boot_file(self):
+ config = self.get_config(self.valid_start_v2 +
+ "u-boot-package = u-boot-linaro-s5pv310\n" \
+ "u-boot-file = ~~\n")
+ self.assertValidationError("Invalid path: ~~", config)
+
+ def test_validate_partition_layout(self):
+ partition_layout = 'apafs_bananfs'
+ config = self.get_config(self.valid_start_v2 + "u-boot-package = " \
+ "u-boot-linaro-s5pv310\nu-boot-file = " \
+ "u-boot.bin\npartition_layout = %s\n" % \
+ partition_layout)
+ self.assertValidationError(
+ "Undefined partition layout %s in the [%s] section. "
+ "Valid partition layouts are %s."
+ % (partition_layout, 'hwpack',
+ ", ".join(config.DEFINED_PARTITION_LAYOUTS)), config)
+
+ 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_serial_tty(self):
+ config = self.get_config(self.valid_start_v2 +
+ "u-boot-package = u-boot-linaro-s5pv310\n" \
+ "u-boot-file = u-boot.bin\nserial_tty=tty\n")
+ self.assertValidationError("Invalid serial tty: tty", config)
+ config = self.get_config(self.valid_start_v2 +
+ "u-boot-package = u-boot-linaro-s5pv310\n" \
+ "u-boot-file = u-boot.bin\n" \
+ "serial_tty=ttxSAC1\n")
+ self.assertValidationError("Invalid serial tty: ttxSAC1", config)
+
+ def test_validate_mmc_id(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "mmc_id = x\n")
+ self.assertValidationError("Invalid mmc id x", config)
+
+ def test_validate_boot_min_size(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "boot_min_size = x\n")
+ self.assertValidationError("Invalid boot min size x", config)
+
+ def test_validate_root_min_size(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "root_min_size = x\n")
+ self.assertValidationError("Invalid root min size x", config)
+
+ def test_validate_loader_min_size(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "loader_min_size = x\n")
+ self.assertValidationError("Invalid loader min size x", config)
+
+ def test_validate_kernel_addr(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "kernel_addr = 0x8000000\n")
+ self.assertValidationError("Invalid kernel address: 0x8000000", config)
+ config = self.get_config(self.valid_complete_v2 +
+ "kernel_addr = 0x8000000x\n")
+ self.assertValidationError("Invalid kernel address: 0x8000000x", config)
+ config = self.get_config(self.valid_complete_v2 +
+ "kernel_addr = 80000000\n")
+ self.assertValidationError("Invalid kernel address: 80000000", config)
+
+ def test_validate_initrd_addr(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "initrd_addr = 0x8000000\n")
+ self.assertValidationError("Invalid initrd address: 0x8000000", config)
+ config = self.get_config(self.valid_complete_v2 +
+ "initrd_addr = 0x8000000x\n")
+ self.assertValidationError("Invalid initrd address: 0x8000000x", config)
+ config = self.get_config(self.valid_complete_v2 +
+ "initrd_addr = 80000000\n")
+ self.assertValidationError("Invalid initrd address: 80000000", config)
+
+ def test_validate_load_addr(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "load_addr = 0x8000000\n")
+ self.assertValidationError("Invalid load address: 0x8000000", config)
+ config = self.get_config(self.valid_complete_v2 +
+ "load_addr = 0x8000000x\n")
+ self.assertValidationError("Invalid load address: 0x8000000x", config)
+ config = self.get_config(self.valid_complete_v2 +
+ "load_addr = 80000000\n")
+ self.assertValidationError("Invalid load address: 80000000", config)
+
+ def test_wired_interfaces(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "wired_interfaces = eth0\n" +
+ self.valid_end)
+ config.validate()
+ self.assertEqual(["eth0"], config.wired_interfaces)
+ config = self.get_config(self.valid_complete_v2 +
+ "wired_interfaces = eth0 eth1 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_v2 +
+ "wireless_interfaces = wlan0\n" +
+ self.valid_end)
+ config.validate()
+ self.assertEqual(["wlan0"], config.wireless_interfaces)
+ config = self.get_config(self.valid_complete_v2 +
+ "wireless_interfaces = wlan0 wl1 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_v2 + 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_v2 + self.valid_end)
+ config.validate()
+ self.assertEqual("usr/lib/u-boot/smdkv310/u-boot.bin",
+ config.u_boot_file)
+
+ def test_serial_tty(self):
+ config = self.get_config(self.valid_complete_v2 + self.valid_end)
+ config.validate()
+ self.assertEqual("ttySAC1", config.serial_tty)
+
+ def test_mmc_id(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "mmc_id = 1\n" +
+ self.valid_end)
+ config.validate()
+ self.assertEqual("1", config.mmc_id)
+
+ def test_boot_min_size(self):
+ config = self.get_config(self.valid_complete_v2 +
+ "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_v2 +
+ "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_v2 +
+ "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_v2 +
+ "kernel_addr = 0x80000000\n" +
+ self.valid_end)
+ config.validate()
+ self.assertEqual("0x80000000", config.kernel_addr)
+ config = self.get_config(self.valid_complete_v2 +
+ "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_v2 +
+ "initrd_addr = 0x80000000\n" +
+ self.valid_end)
+ config.validate()
+ self.assertEqual("0x80000000", config.initrd_addr)
+ config = self.get_config(self.valid_complete_v2 +
+ "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_v2 +
+ "load_addr = 0x80000000\n" +
+ self.valid_end)
+ config.validate()
+ self.assertEqual("0x80000000", config.load_addr)
+ config = self.get_config(self.valid_complete_v2 +
+ "load_addr = 0x8aBcdEFf\n" +
+ self.valid_end)
+ config.validate()
+ self.assertEqual("0x8aBcdEFf", config.load_addr)
+
def test_name(self):
config = self.get_config(
"[hwpack]\nname = ahwpack\npackages = foo\n"
=== modified file 'linaro_image_tools/hwpack/tests/test_hardwarepack.py'
@@ -37,29 +37,32 @@
MatchesStructure,
Not,
)
+from linaro_image_tools.hwpack.hardwarepack_format import (
+ HardwarePackFormatV1,
+ HardwarePackFormatV2,
+ )
class MetadataTests(TestCase):
+ def setUp(self):
+ super(MetadataTests, self).setUp()
+ self.metadata = Metadata("ahwpack", "3", "armel")
def test_name(self):
- metadata = Metadata("ahwpack", "3", "armel")
- self.assertEqual("ahwpack", metadata.name)
+ self.assertEqual("ahwpack", self.metadata.name)
def test_version(self):
- metadata = Metadata("ahwpack", "3", "armel")
- self.assertEqual("3", metadata.version)
+ self.assertEqual("3", self.metadata.version)
def test_version_with_whitespace(self):
self.assertRaises(
AssertionError, Metadata, "ahwpack", "3 (with extras)", "armel")
def test_architecture(self):
- metadata = Metadata("ahwpack", "3", "armel")
- self.assertEqual("armel", metadata.architecture)
+ self.assertEqual("armel", self.metadata.architecture)
def test_default_origin_is_None(self):
- metadata = Metadata("ahwpack", "4", "armel")
- self.assertEqual(None, metadata.origin)
+ self.assertEqual(None, self.metadata.origin)
def test_origin(self):
metadata = Metadata("ahwpack", "4", "armel", origin="linaro")
@@ -108,12 +111,112 @@
"SUPPORT=unsupported\n",
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))
+
+ 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))
+
+ 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))
+
+ 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))
+
+ 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))
+
+ 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))
+
+ 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))
+
+ 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))
+
+ 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))
+
+ 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))
+
+ 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))
+
def test_from_config(self):
class Config:
name = "foo"
origin = "linaro"
maintainer = "someone"
support = "supported"
+ format = HardwarePackFormatV1()
config = Config()
metadata = Metadata.from_config(config, "2.0", "i386")
self.assertEqual(config.name, metadata.name)
@@ -130,14 +233,15 @@
super(HardwarePackTests, self).setUp()
self.metadata = Metadata("ahwpack", "4", "armel")
- def test_format_is_1_0(self):
+ def test_format_is_correct(self):
+ format = '1.0'
hwpack = HardwarePack(self.metadata)
- self.assertEqual("1.0", hwpack.FORMAT)
+ self.assertEqual(format, hwpack.format.__str__())
def test_format_has_no_spaces(self):
hwpack = HardwarePack(self.metadata)
- self.assertIs(None, re.search('\s', hwpack.FORMAT),
- "hwpack.FORMAT contains spaces.")
+ self.assertIs(None, re.search('\s', hwpack.format.__str__()),
+ "hwpack.format contains spaces.")
def test_filename(self):
hwpack = HardwarePack(self.metadata)
@@ -167,7 +271,7 @@
tf = self.get_tarfile(hwpack)
self.assertThat(
tf,
- HardwarePackHasFile("FORMAT", content=hwpack.FORMAT+"\n"))
+ HardwarePackHasFile("FORMAT", content=hwpack.format.__str__()+"\n"))
def test_creates_metadata_file(self):
metadata = Metadata(
=== modified file 'linaro_image_tools/index_server.py'
@@ -22,10 +22,10 @@
import os
import re
-import FetchImage
import urlparse
import logging
import bz2
+import linaro_image_tools.FetchImage
RELEASES_WWW_DOCUMENT_ROOT = "/srv/releases.linaro.org/www/platform/"
RELEASE_URL = "http://releases.linaro.org/platform/"
@@ -41,7 +41,7 @@
def __init__(self):
self.reset()
self.db_file_name = "server_index"
- self.db = FetchImage.DB(self.db_file_name)
+ self.db = linaro_image_tools.FetchImage.DB(self.db_file_name)
def crawl(self):
self.db.set_url_parse_info(self.url_parse)
=== modified file 'linaro_image_tools/media_create/android_boards.py'
@@ -28,6 +28,8 @@
from linaro_image_tools.media_create.boards import PART_ALIGN_S
from linaro_image_tools.media_create.boards import BeagleConfig
from linaro_image_tools.media_create.boards import PandaConfig
+from linaro_image_tools.media_create.boards import SnowballSdConfig
+from linaro_image_tools.media_create.boards import SnowballEmmcConfig
from linaro_image_tools.media_create.boards import (
align_up,
align_partition,
@@ -37,6 +39,7 @@
from linaro_image_tools import cmd_runner
import os
+
class AndroidBoardConfig(object):
@classmethod
def _get_bootargs(cls, consoles):
@@ -78,7 +81,7 @@
as_root=True).wait()
boot_env = cls._get_boot_env(consoles)
- cmdline_filepath = os.path.join(boot_disk, "cmdline")
+ cmdline_filepath = os.path.join(boot_disk, "cmdline")
cmdline_file = open(cmdline_filepath, 'r')
android_kernel_cmdline = cmdline_file.read()
boot_env['bootargs'] = boot_env['bootargs'] + ' ' + \
@@ -96,7 +99,8 @@
pass
@classmethod
- def get_sfdisk_cmd(cls, should_align_boot_part=False):
+ def get_sfdisk_cmd(cls, should_align_boot_part=False,
+ start_addr=0, extra_part=False):
if cls.fat_size == 32:
partition_type = '0x0C'
else:
@@ -116,7 +120,7 @@
# can only start on sector 1 (sector 0 is MBR / partition table)
boot_start, boot_end, boot_len = align_partition(
- 1, BOOT_MIN_SIZE_S, boot_align, PART_ALIGN_S)
+ start_addr + 1, BOOT_MIN_SIZE_S, boot_align, PART_ALIGN_S)
# apparently OMAP3 ROMs require the vfat length to be an even number
# of sectors (multiple of 1 KiB); decrease the length if it's odd,
# there should still be enough room
@@ -131,12 +135,28 @@
_cache_end + 1, USERDATA_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
sdcard_start, _sdcard_end, _sdcard_len = align_partition(
_userdata_end + 1, SDCARD_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
-
+
+ # Snowball board needs a raw partition added to the beginning of image.
+ # If extra_part is True an extra primary partition will be added.
+ # Due to a maximum of 4 primary partitions cache data will be placed in
+ # a extended partition
+ if extra_part == True:
+ assert start_addr > 0, ("Not possible to add extra partition" \
+ "when boot partition starts at '0'")
+ return '%s,%s,%s,*\n%s,%s,L\n%s,-,E\n%s,%s,L\n%s,%s,L\n%s,,,-' % (
+ boot_start, boot_len, partition_type, system_start, _system_len,
+ cache_start, cache_start, _cache_len, userdata_start,
+ _userdata_len, sdcard_start)
+
return '%s,%s,%s,*\n%s,%s,L\n%s,%s,L\n%s,-,E\n%s,%s,L\n%s,,,-' % (
boot_start, boot_len, partition_type, system_start, _system_len,
cache_start, _cache_len, userdata_start, userdata_start,
_userdata_len, sdcard_start)
+ @classmethod
+ def populate_raw_partition(cls, media, boot_dir):
+ super(AndroidBoardConfig, cls).populate_raw_partition(boot_dir, media)
+
class AndroidOmapConfig(AndroidBoardConfig):
pass
@@ -149,10 +169,50 @@
class AndroidPandaConfig(AndroidOmapConfig, PandaConfig):
_extra_serial_opts = 'console=tty0 console=ttyO2,115200n8'
+ extra_boot_args_options = (
+ 'earlyprintk fixrtc nocompcache vram=48M '
+ 'omapfb.vram=0:24M,1:24M mem=456M@0x80000000 mem=512M@0xA0000000')
android_specific_args = 'init=/init androidboot.console=ttyO2'
+class AndroidSnowballSdConfig(AndroidBoardConfig, SnowballSdConfig):
+ extra_boot_args_options = (
+ 'earlyprintk rootdelay=1 fixrtc nocompcache '
+ 'mem=128M@0 mali.mali_mem=64M@128M mem=24M@192M hwmem=167M@216M '
+ 'mem_issw=1M@383M mem=640M@384M vmalloc=256M')
+ _extra_serial_opts = 'console=tty0 console=ttyO2,115200n8'
+ android_specific_args = 'init=/init androidboot.console=ttyAMA2'
+
+
+class AndroidSnowballEmmcConfig(AndroidBoardConfig, SnowballEmmcConfig):
+ extra_boot_args_options = (
+ 'earlyprintk rootdelay=1 fixrtc nocompcache '
+ 'mem=128M@0 mali.mali_mem=64M@128M mem=24M@192M hwmem=167M@216M '
+ 'mem_issw=1M@383M mem=640M@384M vmalloc=256M')
+ _extra_serial_opts = 'console=tty0 console=ttyAMA2,115200n8'
+ android_specific_args = 'init=/init androidboot.console=ttyAMA2'
+
+ @classmethod
+ def get_sfdisk_cmd(cls, should_align_boot_part=False):
+
+ LOADER_MIN_SIZE_S = align_up(
+ 1 * 1024 * 1024, SECTOR_SIZE) / SECTOR_SIZE
+
+ loader_start, loader_end, loader_len = align_partition(
+ SnowballEmmcConfig.SNOWBALL_LOADER_START_S,
+ LOADER_MIN_SIZE_S, 1, PART_ALIGN_S)
+
+ command = super(AndroidSnowballEmmcConfig, cls).get_sfdisk_cmd(
+ should_align_boot_part=True, start_addr=loader_end,
+ extra_part=True)
+
+ return '%s,%s,0xDA\n%s' % (
+ loader_start, loader_len, command)
+
+
android_board_configs = {
'beagle': AndroidBeagleConfig,
'panda': AndroidPandaConfig,
+ 'snowball_sd': AndroidSnowballSdConfig,
+ 'snowball_emmc': AndroidSnowballEmmcConfig,
}
=== modified file 'linaro_image_tools/media_create/boards.py'
@@ -31,11 +31,15 @@
import tempfile
import struct
from binascii import crc32
+import tarfile
+import ConfigParser
+import shutil
from linaro_image_tools import cmd_runner
from linaro_image_tools.media_create.partitions import SECTOR_SIZE
+
KERNEL_GLOB = 'vmlinuz-*-%(kernel_flavor)s'
INITRD_GLOB = 'initrd.img-*-%(kernel_flavor)s'
DTB_GLOB = 'dt-*-%(kernel_flavor)s/%(dtb_name)s'
@@ -58,21 +62,11 @@
# align on 4 MiB
PART_ALIGN_S = 4 * 1024 * 1024 / SECTOR_SIZE
+
def align_up(value, align):
"""Round value to the next multiple of align."""
return (value + align - 1) / align * align
-# optional bootloader partition; at least 1 MiB; in theory, an i.MX5x
-# bootloader partition could hold RedBoot, FIS table, RedBoot config, kernel,
-# and initrd, but we typically use U-Boot which is about 167 KiB as of
-# 2011/02/11 and currently doesn't even store its environment there, so this
-# should be enough
-LOADER_MIN_SIZE_S = align_up(1 * 1024 * 1024, SECTOR_SIZE) / SECTOR_SIZE
-# boot partition; at least 50 MiB; XXX this shouldn't be hardcoded
-BOOT_MIN_SIZE_S = align_up(50 * 1024 * 1024, SECTOR_SIZE) / SECTOR_SIZE
-# root partition; at least 50 MiB; XXX this shouldn't be hardcoded
-ROOT_MIN_SIZE_S = align_up(50 * 1024 * 1024, SECTOR_SIZE) / SECTOR_SIZE
-
# Samsung v310 implementation notes and terminology
#
# * BL0, BL1 etc. are the various bootloaders in order of execution
@@ -104,6 +98,7 @@
assert SAMSUNG_V310_BL2_LEN * SECTOR_SIZE == 512 * 1024, (
"BL1 expects BL2 (u-boot) to be 512 KiB")
+
def align_partition(min_start, min_length, start_alignment, end_alignment):
"""Compute partition start and end offsets based on specified constraints.
@@ -125,10 +120,103 @@
"""A descriptor that provides @property behavior on class methods."""
def __init__(self, getter):
self.getter = getter
+
def __get__(self, instance, cls):
return self.getter(cls)
+class HardwarepackHandler(object):
+ FORMAT_1 = '1.0'
+ FORMAT_2 = '2.0'
+ FORMAT_MIXED = '1.0and2.0'
+ metadata_filename = 'metadata'
+ format_filename = 'FORMAT'
+ main_section = 'main'
+ hwpack_tarfiles = []
+ tempdir = None
+
+ def __init__(self, hwpacks):
+ self.hwpacks = hwpacks
+ self.hwpack_tarfiles = []
+
+ class FakeSecHead(object):
+ """ Add a fake section header to the metadata file.
+
+ This is done so we can use ConfigParser to parse the file.
+ """
+ def __init__(self, fp):
+ self.fp = fp
+ self.sechead = '[%s]\n' % HardwarepackHandler.main_section
+
+ def readline(self):
+ if self.sechead:
+ try:
+ return self.sechead
+ finally:
+ self.sechead = None
+ else:
+ return self.fp.readline()
+
+ def __enter__(self):
+ self.tempdir = tempfile.mkdtemp()
+ for hwpack in self.hwpacks:
+ hwpack_tarfile = tarfile.open(hwpack, mode='r:gz')
+ self.hwpack_tarfiles.append(hwpack_tarfile)
+ return self
+
+ def __exit__(self, type, value, traceback):
+ for hwpack_tarfile in self.hwpack_tarfiles:
+ if hwpack_tarfile is not None:
+ hwpack_tarfile.close()
+ self.hwpack_tarfiles = []
+ if self.tempdir is not None and os.path.exists(self.tempdir):
+ shutil.rmtree(self.tempdir)
+
+ def get_field(self, section, 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))
+ try:
+ new_data = parser.get(section, 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, new_data)
+ data = new_data
+ 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]
+ for hwpack_tarfile in self.hwpack_tarfiles:
+ format_file = hwpack_tarfile.extractfile(self.format_filename)
+ format_string = format_file.read().strip()
+ if not format_string in supported_formats:
+ raise AssertionError(
+ "Format version '%s' is not supported." % \
+ format_string)
+ if format is None:
+ format = format_string
+ elif format != format_string:
+ return self.FORMAT_MIXED
+ return format
+
+ def get_file(self, file_alias):
+ file_name, hwpack_tarfile = self.get_field(self.main_section,
+ file_alias)
+ if file_name is not None:
+ hwpack_tarfile.extract(file_name, self.tempdir)
+ file_name = os.path.join(self.tempdir, file_name)
+ return file_name
+
+
class BoardConfig(object):
"""The configuration used when building an image for a board."""
# These attributes may not need to be redefined on some subclasses.
@@ -138,12 +226,17 @@
mmc_option = '0:1'
mmc_part_offset = 0
fat_size = 32
- extra_serial_opts = ''
- live_serial_opts = ''
+ _extra_serial_opts = ''
+ _live_serial_opts = ''
extra_boot_args_options = None
supports_writing_to_mmc = True
+ LOADER_MIN_SIZE_S = align_up(1 * 1024**2, SECTOR_SIZE) / SECTOR_SIZE
+ BOOT_MIN_SIZE_S = align_up(50 * 1024**2, SECTOR_SIZE) / SECTOR_SIZE
+ ROOT_MIN_SIZE_S = align_up(50 * 1024**2, SECTOR_SIZE) / SECTOR_SIZE
- # These attributes must be defined on all subclasses.
+ # These attributes must be defined on all subclasses for backwards
+ # compatibility with hwpacks v1 format. Hwpacks v2 format allows these to
+ # be specified in the hwpack metadata.
kernel_addr = None
initrd_addr = None
load_addr = None
@@ -152,6 +245,88 @@
kernel_flavors = None
boot_script = None
serial_tty = None
+ wired_interfaces = None
+ wireless_interfaces = None
+ mmc_id = None
+
+ hardwarepack_handler = None
+
+ @classmethod
+ def get_metadata_field(cls, target, 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)
+ return data
+
+ @classmethod
+ def set_metadata(cls, hwpacks):
+ cls.hardwarepack_handler = HardwarepackHandler(hwpacks)
+ with cls.hardwarepack_handler:
+ if (cls.hardwarepack_handler.get_format() ==
+ cls.hardwarepack_handler.FORMAT_1):
+ return
+
+ if (cls.hardwarepack_handler.get_format() ==
+ cls.hardwarepack_handler.FORMAT_2):
+ # Clear V1 defaults.
+ cls.kernel_addr = None
+ cls.initrd_addr = None
+ cls.load_addr = None
+ cls.serial_tty = None
+ cls.fat_size = None
+ cls.BOOT_MIN_SIZE_S = None
+ cls.ROOT_MIN_SIZE_S = None
+ cls.LOADER_MIN_SIZE_S = None
+
+ # Set new values from metadata.
+ cls.kernel_addr = cls.get_metadata_field(
+ cls.kernel_addr, 'kernel_addr')
+ cls.initrd_addr = cls.get_metadata_field(
+ cls.initrd_addr, 'initrd_addr')
+ cls.load_addr = cls.get_metadata_field(
+ cls.load_addr, 'load_addr')
+ cls.serial_tty = cls.get_metadata_field(
+ cls.serial_tty, 'serial_tty')
+ cls.wired_interfaces = cls.get_metadata_field(
+ cls.wired_interfaces, 'wired_interfaces')
+ cls.wireless_interfaces = cls.get_metadata_field(
+ cls.wireless_interfaces, 'wireless_interfaces')
+ cls.mmc_id = cls.get_metadata_field(
+ cls.mmc_id, 'mmc_id')
+
+ partition_layout = cls.get_metadata_field(cls.fat_size, 'partition_layout')
+ if partition_layout == 'bootfs_rootfs' or partition_layout is None:
+ cls.fat_size = 32
+ elif partition_layout == 'bootfs16_rootfs':
+ cls.fat_size = 16
+ else:
+ raise AssertionError("Unknown partition layout '%s'." % partition_layout)
+
+ boot_min_size = cls.get_metadata_field(
+ cls.BOOT_MIN_SIZE_S, 'boot_min_size')
+ if boot_min_size is not None:
+ cls.BOOT_MIN_SIZE_S = align_up(int(boot_min_size) * 1024**2,
+ SECTOR_SIZE) / SECTOR_SIZE
+ root_min_size = cls.get_metadata_field(
+ cls.ROOT_MIN_SIZE_S, 'root_min_size')
+ if root_min_size is not None:
+ cls.ROOT_MIN_SIZE_S = align_up(int(root_min_size) * 1024**2,
+ SECTOR_SIZE) / SECTOR_SIZE
+ loader_min_size = cls.get_metadata_field(
+ cls.LOADER_MIN_SIZE_S, 'loader_min_size')
+ if loader_min_size is not None:
+ cls.LOADER_MIN_SIZE_S = align_up(int(loader_min_size) * 1024**2,
+ SECTOR_SIZE) / SECTOR_SIZE
+
+
+ @classmethod
+ def get_file(cls, file_alias, default=None):
+ file_in_hwpack = cls.hardwarepack_handler.get_file(file_alias)
+ if file_in_hwpack is not None:
+ return file_in_hwpack
+ else:
+ return default
@classmethod
def get_sfdisk_cmd(cls, should_align_boot_part=False):
@@ -167,9 +342,6 @@
else:
partition_type = '0x0E'
- BOOT_MIN_SIZE_S = align_up(50 * 1024 * 1024, SECTOR_SIZE) / SECTOR_SIZE
- ROOT_MIN_SIZE_S = align_up(50 * 1024 * 1024, SECTOR_SIZE) / SECTOR_SIZE
-
# align on sector 63 for compatibility with broken versions of x-loader
# unless align_boot_part is set
boot_align = 63
@@ -178,7 +350,7 @@
# can only start on sector 1 (sector 0 is MBR / partition table)
boot_start, boot_end, boot_len = align_partition(
- 1, BOOT_MIN_SIZE_S, boot_align, PART_ALIGN_S)
+ 1, cls.BOOT_MIN_SIZE_S, boot_align, PART_ALIGN_S)
# apparently OMAP3 ROMs require the vfat length to be an even number
# of sectors (multiple of 1 KiB); decrease the length if it's odd,
# there should still be enough room
@@ -189,7 +361,7 @@
# instruct the use of all remaining space; XXX if we had some root size
# config, we could do something more sensible
root_start, _root_end, _root_len = align_partition(
- boot_end + 1, ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
+ boot_end + 1, cls.ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
return '%s,%s,%s,*\n%s,,,-' % (
boot_start, boot_len, partition_type, root_start)
@@ -299,10 +471,12 @@
if cls.uboot_in_boot_part:
assert cls.uboot_flavor is not None, (
"uboot_in_boot_part is set but not uboot_flavor")
- uboot_bin = os.path.join(chroot_dir, 'usr', 'lib', 'u-boot',
- cls.uboot_flavor, 'u-boot.bin')
- cmd_runner.run(
- ['cp', '-v', uboot_bin, boot_disk], as_root=True).wait()
+ with cls.hardwarepack_handler:
+ uboot_bin = cls.get_file('u_boot', default=os.path.join(
+ chroot_dir, 'usr', 'lib', 'u-boot', cls.uboot_flavor,
+ 'u-boot.bin'))
+ cmd_runner.run(
+ ['cp', '-v', uboot_bin, boot_disk], as_root=True).wait()
cls.make_boot_files(
uboot_parts_dir, is_live, is_lowmem, consoles, chroot_dir,
@@ -337,9 +511,14 @@
"No kernel found matching %s for flavors %s" % (
KERNEL_GLOB, " ".join(cls.kernel_flavors)))
+ @classmethod
+ def populate_raw_partition(cls, media, boot_dir):
+ # Override in subclass if needed
+ pass
+
class OmapConfig(BoardConfig):
- kernel_flavors = ['linaro-omap4', 'linaro-omap', 'omap4']
+ kernel_flavors = ['linaro-omap4', 'linaro-lt-omap', 'linaro-omap', 'omap4']
uboot_in_boot_part = True
# XXX: Here we define these things as dynamic properties because our
@@ -471,8 +650,8 @@
class Ux500Config(BoardConfig):
serial_tty = 'ttyAMA2'
- extra_serial_opts = 'console=tty0 console=%s,115200n8' % serial_tty
- live_serial_opts = 'serialtty=%s' % serial_tty
+ _extra_serial_opts = 'console=tty0 console=%s,115200n8'
+ _live_serial_opts = 'serialtty=%s'
kernel_addr = '0x00100000'
initrd_addr = '0x08000000'
load_addr = '0x00008000'
@@ -485,6 +664,14 @@
'hwmem=48M@302M mem=152M@360M')
mmc_option = '1:1'
+ @classproperty
+ def live_serial_opts(cls):
+ return cls._live_serial_opts % cls.serial_tty
+
+ @classproperty
+ def extra_serial_opts(cls):
+ return cls._extra_serial_opts % cls.serial_tty
+
@classmethod
def _make_boot_files(cls, boot_env, chroot_dir, boot_dir,
boot_device_or_file, k_img_data, i_img_data,
@@ -517,7 +704,7 @@
and u-boot.'''
# Boot ROM looks for a boot table of contents (TOC) at 0x20000
# Actually, it first looks at address 0, but that's where l-m-c
- # puts the MBR, so the boot loader skips that address.
+ # puts the MBR, so the boot loader skips that address.
supports_writing_to_mmc = False
SNOWBALL_LOADER_START_S = (128 * 1024) / SECTOR_SIZE
SNOWBALL_STARTUP_FILES_CONFIG = 'startfiles.cfg'
@@ -537,20 +724,20 @@
This is done since the boot rom always boots off the internal memory;
there simply is no point to having a loader partition on SD card.
"""
- # boot ROM expects bootloader at 0x20000, which is sector 0x100
+ # boot ROM expects bootloader at 0x20000, which is sector 0x100
# with the usual SECTOR_SIZE of 0x200.
# (sector 0 is MBR / partition table)
loader_start, loader_end, loader_len = align_partition(
- SnowballEmmcConfig.SNOWBALL_LOADER_START_S,
- LOADER_MIN_SIZE_S, 1, PART_ALIGN_S)
+ SnowballEmmcConfig.SNOWBALL_LOADER_START_S,
+ cls.LOADER_MIN_SIZE_S, 1, PART_ALIGN_S)
boot_start, boot_end, boot_len = align_partition(
- loader_end + 1, BOOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
+ loader_end + 1, cls.BOOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
# we ignore _root_end / _root_len and return an sfdisk command to
# instruct the use of all remaining space; XXX if we had some root size
# config, we could do something more sensible
root_start, _root_end, _root_len = align_partition(
- boot_end + 1, ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
+ boot_end + 1, cls.ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
return '%s,%s,0xDA\n%s,%s,0x0C,*\n%s,,,-' % (
loader_start, loader_len, boot_start, boot_len, root_start)
@@ -562,15 +749,22 @@
make_uImage(cls.load_addr, k_img_data, boot_dir)
boot_script_path = os.path.join(boot_dir, cls.boot_script)
make_boot_script(boot_env, boot_script_path)
+ cls.populate_raw_partition(chroot_dir, boot_device_or_file)
+
+ @classmethod
+ def populate_raw_partition(cls, chroot_dir, boot_device_or_file):
+ # Populate created raw partition with TOC and startup files.
+ config_files_path = os.path.join(chroot_dir, 'boot')
_, toc_filename = tempfile.mkstemp()
- atexit.register(os.unlink, toc_filename)
- config_files_path = os.path.join(chroot_dir, 'boot')
new_files = cls.get_file_info(config_files_path)
with open(toc_filename, 'wb') as toc:
cls.create_toc(toc, new_files)
cls.install_snowball_boot_loader(toc_filename, new_files,
boot_device_or_file,
cls.SNOWBALL_LOADER_START_S)
+ cls.delete_file(toc_filename)
+ cls.delete_file(os.path.join(config_files_path,
+ cls.SNOWBALL_STARTUP_FILES_CONFIG))
@classmethod
def install_snowball_boot_loader(cls, toc_file_name, files,
@@ -584,13 +778,21 @@
for file in files:
# XXX We need checks that these files do not overwrite each
# other. This code assumes that offset and file sizes are ok.
+ filename = file['filename']
if (file['offset'] % SECTOR_SIZE) != 0:
seek_bytes = start_sector * SECTOR_SIZE + file['offset']
- _dd(file['filename'], boot_device_or_file, block_size=1,
+ _dd(filename, boot_device_or_file, block_size=1,
seek=seek_bytes)
else:
- seek_sectors = start_sector + file['offset']/SECTOR_SIZE
- _dd(file['filename'], boot_device_or_file, seek=seek_sectors)
+ seek_sectors = start_sector + file['offset'] / SECTOR_SIZE
+ _dd(filename, boot_device_or_file, seek=seek_sectors)
+ cls.delete_file(filename)
+
+ @classmethod
+ def delete_file(cls, file_path):
+ cmd = ["rm", "%s" % file_path]
+ proc = cmd_runner.run(cmd, as_root=True)
+ proc.wait()
@classmethod
def create_toc(cls, f, files):
@@ -605,6 +807,8 @@
# i; int; load_address,
# 12s; string of char; name
# http://igloocommunity.org/support/index.php/ConfigPartitionOverview
+ assert len(file['section_name']) < 12, (
+ "Section name %s too large" % file['section_name'])
flags = 0
load_adress = file['align']
data = struct.pack('<IIIii12s', file['offset'], file['size'],
@@ -642,12 +846,20 @@
class Mx5Config(BoardConfig):
serial_tty = 'ttymxc0'
- extra_serial_opts = 'console=tty0 console=%s,115200n8' % serial_tty
- live_serial_opts = 'serialtty=%s' % serial_tty
+ _extra_serial_opts = 'console=tty0 console=%s,115200n8'
+ _live_serial_opts = 'serialtty=%s'
boot_script = 'boot.scr'
mmc_part_offset = 1
mmc_option = '0:2'
+ @classproperty
+ def live_serial_opts(cls):
+ return cls._live_serial_opts % cls.serial_tty
+
+ @classproperty
+ def extra_serial_opts(cls):
+ return cls._extra_serial_opts % cls.serial_tty
+
@classmethod
def get_sfdisk_cmd(cls, should_align_boot_part=None):
"""Return the sfdisk command to partition the media.
@@ -663,15 +875,15 @@
# onwards, so it's safer to just start at the first sector, sector 1
# (sector 0 is MBR / partition table)
loader_start, loader_end, loader_len = align_partition(
- 1, LOADER_MIN_SIZE_S, 1, PART_ALIGN_S)
+ 1, cls.LOADER_MIN_SIZE_S, 1, PART_ALIGN_S)
boot_start, boot_end, boot_len = align_partition(
- loader_end + 1, BOOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
+ loader_end + 1, cls.BOOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
# we ignore _root_end / _root_len and return a sfdisk command to
# instruct the use of all remaining space; XXX if we had some root size
# config, we could do something more sensible
root_start, _root_end, _root_len = align_partition(
- boot_end + 1, ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
+ boot_end + 1, cls.ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
return '%s,%s,0xDA\n%s,%s,0x0C,*\n%s,,,-' % (
loader_start, loader_len, boot_start, boot_len, root_start)
@@ -680,9 +892,12 @@
def _make_boot_files(cls, boot_env, chroot_dir, boot_dir,
boot_device_or_file, k_img_data, i_img_data,
d_img_data):
- uboot_file = 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)
+ with cls.hardwarepack_handler:
+ uboot_file = cls.get_file('u_boot', 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,
+ cls.LOADER_MIN_SIZE_S)
make_uImage(cls.load_addr, k_img_data, boot_dir)
make_uInitrd(i_img_data, boot_dir)
make_dtb(d_img_data, boot_dir)
@@ -730,8 +945,8 @@
uboot_flavor = 'ca9x4_ct_vxp'
uboot_in_boot_part = True
serial_tty = 'ttyAMA0'
- extra_serial_opts = 'console=tty0 console=%s,38400n8' % serial_tty
- live_serial_opts = 'serialtty=%s' % serial_tty
+ _extra_serial_opts = 'console=tty0 console=%s,38400n8'
+ _live_serial_opts = 'serialtty=%s'
kernel_addr = '0x60008000'
initrd_addr = '0x81000000'
load_addr = kernel_addr
@@ -741,6 +956,14 @@
# only allows for FAT16
fat_size = 16
+ @classproperty
+ def live_serial_opts(cls):
+ return cls._live_serial_opts % cls.serial_tty
+
+ @classproperty
+ def extra_serial_opts(cls):
+ return cls._extra_serial_opts % cls.serial_tty
+
@classmethod
def _make_boot_files(cls, boot_env, chroot_dir, boot_dir,
boot_device_or_file, k_img_data, i_img_data,
@@ -748,10 +971,11 @@
make_uImage(cls.load_addr, k_img_data, boot_dir)
make_uInitrd(i_img_data, boot_dir)
+
class SMDKV310Config(BoardConfig):
uboot_flavor = 'smdkv310'
serial_tty = 'ttySAC1'
- extra_serial_opts = 'console=%s,115200n8' % serial_tty
+ _extra_serial_opts = 'console=%s,115200n8'
kernel_addr = '0x40007000'
initrd_addr = '0x42000000'
load_addr = '0x40008000'
@@ -760,6 +984,10 @@
mmc_part_offset = 1
mmc_option = '0:2'
+ @classproperty
+ def extra_serial_opts(cls):
+ return cls._extra_serial_opts % cls.serial_tty
+
@classmethod
def get_sfdisk_cmd(cls, should_align_boot_part=False):
# bootloaders partition needs to hold BL1, U-Boot environment, and BL2
@@ -773,14 +1001,14 @@
# FAT boot partition
boot_start, boot_end, boot_len = align_partition(
- loaders_end + 1, BOOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
+ loaders_end + 1, cls.BOOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
# root partition
# we ignore _root_end / _root_len and return a sfdisk command to
# instruct the use of all remaining space; XXX if we had some root size
# config, we could do something more sensible
root_start, _root_end, _root_len = align_partition(
- boot_end + 1, ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
+ boot_end + 1, cls.ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S)
return '%s,%s,0xDA\n%s,%s,0x0C,*\n%s,,,-' % (
loaders_start, loaders_len, boot_start, boot_len, root_start)
@@ -800,19 +1028,8 @@
def _make_boot_files(cls, boot_env, chroot_dir, boot_dir,
boot_device_or_file, k_img_data, i_img_data,
d_img_data):
- spl_file = os.path.join(
- chroot_dir, 'usr', 'lib', 'u-boot', cls.uboot_flavor,
- 'v310_mmc_spl.bin')
- # XXX need to check that the length of spl_file is smaller than
- # SAMSUNG_V310_BL1_LEN
- _dd(spl_file, boot_device_or_file, seek=SAMSUNG_V310_BL1_START)
-
- uboot_file = os.path.join(
- chroot_dir, 'usr', 'lib', 'u-boot', cls.uboot_flavor, 'u-boot.bin')
- # XXX need to check that the length of uboot_file is smaller than
- # SAMSUNG_V310_BL2_LEN
- _dd(uboot_file, boot_device_or_file, seek=SAMSUNG_V310_BL2_START)
-
+ cls.install_smdk_boot_loader(chroot_dir, boot_device_or_file,
+ cls.uboot_flavor)
env_size = SAMSUNG_V310_ENV_LEN * SECTOR_SIZE
env_file = make_flashable_env(boot_env, env_size)
_dd(env_file, boot_device_or_file, seek=SAMSUNG_V310_ENV_START)
@@ -825,6 +1042,45 @@
boot_script_path = os.path.join(boot_dir, cls.boot_script)
make_boot_script(boot_env, boot_script_path)
+ @classmethod
+ def _get_smdk_spl(cls, chroot_dir, uboot_flavor):
+ spl_dir = os.path.join(
+ chroot_dir, 'usr', 'lib', 'u-boot', uboot_flavor)
+ old_spl_path = os.path.join(spl_dir, 'v310_mmc_spl.bin')
+ new_spl_path = os.path.join(spl_dir, 'u-boot-mmc-spl.bin')
+
+ spl_file = old_spl_path
+ # The new upstream u-boot filename has changed
+ if not os.path.exists(spl_file):
+ spl_file = new_spl_path
+
+ if not os.path.exists(spl_file):
+ # missing SPL loader
+ raise AssertionError("Couldn't find the SPL file, tried %s and %s"
+ % (old_spl_path, new_spl_path))
+ return spl_file
+
+ @classmethod
+ def _get_smdk_uboot(cls, chroot_dir, uboot_flavor):
+ uboot_file = os.path.join(
+ chroot_dir, 'usr', 'lib', 'u-boot', uboot_flavor, 'u-boot.bin')
+ return uboot_file
+
+ @classmethod
+ def install_smdk_boot_loader(cls, chroot_dir, boot_device_or_file,
+ uboot_flavor):
+ spl_file = cls._get_smdk_spl(chroot_dir, uboot_flavor)
+ assert os.path.getsize(spl_file) <= SAMSUNG_V310_BL1_LEN, (
+ "%s is larger than SAMSUNG_V310_BL1_LEN" % spl_file)
+ _dd(spl_file, boot_device_or_file, seek=SAMSUNG_V310_BL1_START)
+
+ with cls.hardwarepack_handler:
+ uboot_file = cls.get_file('u_boot', default=cls._get_smdk_uboot(
+ chroot_dir, uboot_flavor))
+ assert os.path.getsize(uboot_file) <= SAMSUNG_V310_BL2_LEN, (
+ "%s is larger than SAMSUNG_V310_BL2_LEN" % uboot_file)
+ _dd(uboot_file, boot_device_or_file, seek=SAMSUNG_V310_BL2_START)
+
board_configs = {
'beagle': BeagleConfig,
@@ -837,7 +1093,7 @@
'efikamx': EfikamxConfig,
'efikasb': EfikasbConfig,
'mx51evk': Mx51evkConfig,
- 'mx53loco' : Mx53LoCoConfig,
+ 'mx53loco': Mx53LoCoConfig,
'overo': OveroConfig,
'smdkv310': SMDKV310Config,
}
@@ -961,13 +1217,13 @@
return tmpfile
-def install_mx5_boot_loader(imx_file, boot_device_or_file):
+def install_mx5_boot_loader(imx_file, boot_device_or_file, loader_min_size):
# bootloader partition starts at +1s but we write the file at +2s, so we
# need to check that the bootloader partition minus 1s is at least as large
# as the u-boot binary; note that the real bootloader partition might be
# larger than LOADER_MIN_SIZE_S, but if u-boot is larger it's a sign we
# need to bump LOADER_MIN_SIZE_S
- max_size = (LOADER_MIN_SIZE_S - 1) * SECTOR_SIZE
+ max_size = (loader_min_size - 1) * SECTOR_SIZE
assert os.path.getsize(imx_file) <= max_size, (
"%s is larger than guaranteed bootloader partition size" % imx_file)
_dd(imx_file, boot_device_or_file, seek=2)
@@ -1008,4 +1264,3 @@
["cp", "-v", boot_script_path, "%s/boot.ini" % boot_disk],
as_root=True)
proc.wait()
-
=== modified file 'linaro_image_tools/media_create/partitions.py'
@@ -3,7 +3,7 @@
# Author: Guilherme Salgado <guilherme.salgado@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 3 of the License, or
@@ -28,6 +28,7 @@
Device,
Disk,
PARTITION_NORMAL,
+ PARTITION_EXTENDED,
)
from linaro_image_tools import cmd_runner
@@ -71,9 +72,8 @@
bootfs = partitions[0]
system = partitions[1]
cache = partitions[2]
- data = partitions[4]
- sdcard = partitions[5]
-
+ data = partitions[3]
+ sdcard = partitions[4]
print "\nFormating boot partition\n"
proc = cmd_runner.run(
@@ -98,6 +98,7 @@
return bootfs, system, cache, data, sdcard
+
# I wonder if it'd make sense to convert this into a small shim which calls
# the appropriate function for the given type of device? I think it's still
# small enough that there's not much benefit in doing that, but if it grows we
@@ -296,18 +297,28 @@
# Here we can use parted.Device to read the partitions because we're
# reading from a regular file rather than a block device. If it was a
# block device we'd need root rights.
+ vfat_partition = None
disk = Disk(Device(image_file))
partition_info = []
for partition in disk.partitions:
- geometry = partition.geometry
- partition_info.append((geometry.start * SECTOR_SIZE,
- geometry.length * SECTOR_SIZE))
+ # Will ignore any partitions before boot and of type EXTENDED
+ if 'boot' in partition.getFlagsAsString():
+ vfat_partition = partition
+ geometry = partition.geometry
+ partition_info.append((geometry.start * SECTOR_SIZE,
+ geometry.length * SECTOR_SIZE))
+ elif (vfat_partition is not None and
+ partition.type != PARTITION_EXTENDED):
+ geometry = partition.geometry
+ partition_info.append((geometry.start * SECTOR_SIZE,
+ geometry.length * SECTOR_SIZE))
# NB: don't use vfat_partition.nextPartition() as that might return
# a partition of type PARTITION_FREESPACE; it's much easier to
# iterate disk.partitions which only returns
# parted.PARTITION_NORMAL partitions
-
- assert len(partition_info) == 6
+ assert vfat_partition is not None, (
+ "Couldn't find boot partition on %s" % image_file)
+ assert len(partition_info) == 5
return partition_info
@@ -347,6 +358,7 @@
return boot_partition, system_partition, cache_partition, \
data_partition, sdcard_partition
+
def get_boot_and_root_partitions_for_media(media, board_config):
"""Return the device files for the boot and root partitions of media.
=== modified file 'linaro_image_tools/media_create/tests/test_media_create.py'
@@ -28,7 +28,10 @@
import textwrap
import time
import types
+import struct
+import tarfile
+from StringIO import StringIO
from testtools import TestCase
from linaro_image_tools import cmd_runner
@@ -38,9 +41,11 @@
boards,
partitions,
rootfs,
+ android_boards,
)
from linaro_image_tools.media_create.boards import (
- LOADER_MIN_SIZE_S,
+ SAMSUNG_V310_BL1_START,
+ SAMSUNG_V310_BL2_START,
SECTOR_SIZE,
align_up,
align_partition,
@@ -56,6 +61,11 @@
_get_file_matching,
_get_mlo_file,
_run_mkimage,
+ HardwarepackHandler,
+ BoardConfig,
+ )
+from linaro_image_tools.media_create.android_boards import (
+ android_board_configs,
)
from linaro_image_tools.media_create.chroot_utils import (
copy_file,
@@ -113,6 +123,359 @@
sudo_args = " ".join(cmd_runner.SUDO_ARGS)
+class TestHardwarepackHandler(TestCaseWithFixtures):
+ def setUp(self):
+ super(TestHardwarepackHandler, self).setUp()
+ self.tar_dir_fixture = CreateTempDirFixture()
+ self.useFixture(self.tar_dir_fixture)
+
+ self.tarball_fixture = CreateTarballFixture(
+ self.tar_dir_fixture.get_temp_dir())
+ self.useFixture(self.tarball_fixture)
+
+ self.metadata = (
+ "NAME=ahwpack\nVERSION=4\nARCHITECTURE=armel\nORIGIN=linaro\n")
+
+ def add_to_tarball(self, files, tarball=None):
+ if tarball is None:
+ tarball = self.tarball_fixture.get_tarball()
+ tar_file = tarfile.open(tarball, mode='w:gz')
+ for filename, data in files:
+ tarinfo = tarfile.TarInfo(filename)
+ tarinfo.size = len(data)
+ tar_file.addfile(tarinfo, StringIO(data))
+ tar_file.close()
+ return tarball
+
+ def test_get_format_1(self):
+ data = '1.0'
+ format = "%s\n" % data
+ tarball = self.add_to_tarball(
+ [('FORMAT', format), ('metadata', self.metadata)])
+ hp = HardwarepackHandler([tarball])
+ with hp:
+ self.assertEquals(hp.get_format(), data)
+
+ def test_get_format_2(self):
+ data = '2.0'
+ format = "%s\n" % data
+ tarball = self.add_to_tarball(
+ [('FORMAT', format), ('metadata', self.metadata)])
+ hp = HardwarepackHandler([tarball])
+ with hp:
+ self.assertEquals(hp.get_format(), data)
+
+ def test_get_unknown_format_raises(self):
+ data = '9.9'
+ format = "%s\n" % data
+ tarball = self.add_to_tarball(
+ [('FORMAT', format), ('metadata', self.metadata)])
+ hp = HardwarepackHandler([tarball])
+ with hp:
+ self.assertRaises(AssertionError, hp.get_format)
+
+ def test_mixed_formats(self):
+ format1 = "%s\n" % '1.0'
+ format2 = "%s\n" % '2.0'
+ tarball1 = self.add_to_tarball(
+ [('FORMAT', format1), ('metadata', self.metadata)],
+ tarball=self.tarball_fixture.get_tarball())
+ tarball_fixture2 = CreateTarballFixture(
+ self.tar_dir_fixture.get_temp_dir(), reldir='tarfile2',
+ filename='secondtarball.tar.gz')
+ self.useFixture(tarball_fixture2)
+ tarball2 = self.add_to_tarball(
+ [('FORMAT', format2), ('metadata', self.metadata)],
+ tarball=tarball_fixture2.get_tarball())
+ hp = HardwarepackHandler([tarball2, tarball1])
+ with hp:
+ self.assertEquals(hp.get_format(), '1.0and2.0')
+
+ def test_identical_formats_ok(self):
+ format1 = "%s\n" % '2.0'
+ format2 = "%s\n" % '2.0'
+ tarball1 = self.add_to_tarball(
+ [('FORMAT', format1), ('metadata', self.metadata)],
+ tarball=self.tarball_fixture.get_tarball())
+ tarball_fixture2 = CreateTarballFixture(
+ self.tar_dir_fixture.get_temp_dir(), reldir='tarfile2',
+ filename='secondtarball.tar.gz')
+ self.useFixture(tarball_fixture2)
+ tarball2 = self.add_to_tarball(
+ [('FORMAT', format2), ('metadata', self.metadata)],
+ tarball=tarball_fixture2.get_tarball())
+ hp = HardwarepackHandler([tarball1, tarball2])
+ with hp:
+ self.assertEquals(hp.get_format(), '2.0')
+
+ def test_get_metadata(self):
+ data = 'data to test'
+ metadata = self.metadata + "TEST=%s\n" % data
+ tarball = self.add_to_tarball(
+ [('metadata', metadata)])
+ hp = HardwarepackHandler([tarball])
+ with hp:
+ test_data, _ = hp.get_field(hp.main_section, 'test')
+ self.assertEqual(test_data, data)
+
+ def test_preserves_formatters(self):
+ data = '%s%d'
+ metadata = self.metadata + "TEST=%s\n" % data
+ tarball = self.add_to_tarball(
+ [('metadata', metadata)])
+ hp = HardwarepackHandler([tarball])
+ with hp:
+ test_data, _ = hp.get_field(hp.main_section, 'test')
+ self.assertEqual(test_data, data)
+
+ def test_creates_tempdir(self):
+ tarball = self.add_to_tarball(
+ [('metadata', self.metadata)])
+ hp = HardwarepackHandler([tarball])
+ with hp:
+ self.assertTrue(os.path.exists(hp.tempdir))
+
+ def test_tempfiles_are_removed(self):
+ tempdir = None
+ tarball = self.add_to_tarball(
+ [('metadata', self.metadata)])
+ hp = HardwarepackHandler([tarball])
+ with hp:
+ tempdir = hp.tempdir
+ self.assertFalse(os.path.exists(tempdir))
+
+ 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)
+ tarball = self.add_to_tarball(
+ [('metadata', metadata),
+ (file_in_archive, data)])
+ hp = HardwarepackHandler([tarball])
+ with hp:
+ test_file = hp.get_file(metadata_file)
+ self.assertEquals(data, open(test_file, 'r').read())
+
+
+class TestSetMetadata(TestCaseWithFixtures):
+
+ class MockHardwarepackHandler(HardwarepackHandler):
+ metadata_dict = {}
+
+ def __enter__(self):
+ return self
+
+ def get_field(self, section, field):
+ try:
+ return self.metadata_dict[field], None
+ except:
+ return None, None
+
+ def get_format(self):
+ return '2.0'
+
+ def get_file(self, file_alias):
+ return None
+
+ def test_does_not_set_if_old_format(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(None, config.kernel_addr)
+
+ def test_sets_kernel_addr(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'kernel_addr'
+ data_to_set = '0x8123ABCD'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(data_to_set, config.kernel_addr)
+
+ def test_sets_initrd_addr(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'initrd_addr'
+ data_to_set = '0x8123ABCD'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(data_to_set, config.initrd_addr)
+
+ def test_sets_load_addr(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'load_addr'
+ data_to_set = '0x8123ABCD'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(data_to_set, config.load_addr)
+
+ def test_sets_serial_tty(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'serial_tty'
+ data_to_set = 'ttyAA'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(data_to_set, config.serial_tty)
+
+ def test_sets_wired_interfaces(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'wired_interfaces'
+ data_to_set = 'eth0 eth1'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(data_to_set, config.wired_interfaces)
+
+ def test_sets_wireless_interfaces(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'wireless_interfaces'
+ data_to_set = 'wlan0 wl1'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(data_to_set, config.wireless_interfaces)
+
+ def test_sets_mmc_id(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'mmc_id'
+ data_to_set = '1'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(data_to_set, config.mmc_id)
+
+ def test_sets_boot_min_size(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'boot_min_size'
+ data_to_set = '100'
+ expected = align_up(int(data_to_set) * 1024 * 1024,
+ SECTOR_SIZE) / SECTOR_SIZE
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(expected, config.BOOT_MIN_SIZE_S)
+
+ def test_sets_root_min_size(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'root_min_size'
+ data_to_set = '3'
+ expected = align_up(int(data_to_set) * 1024 * 1024,
+ SECTOR_SIZE) / SECTOR_SIZE
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(expected, config.ROOT_MIN_SIZE_S)
+
+ def test_sets_loader_min_size(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'loader_min_size'
+ data_to_set = '2'
+ expected = align_up(int(data_to_set) * 1024 * 1024,
+ SECTOR_SIZE) / SECTOR_SIZE
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(expected, config.LOADER_MIN_SIZE_S)
+
+ def test_sets_partition_layout_32(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'partition_layout'
+ data_to_set = 'bootfs_rootfs'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(32, config.fat_size)
+
+ def test_sets_partition_layout_16(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'partition_layout'
+ data_to_set = 'bootfs16_rootfs'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ config.set_metadata('ahwpack.tar.gz')
+ self.assertEquals(16, config.fat_size)
+
+ def test_sets_partition_layout_raises(self):
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards, 'HardwarepackHandler',
+ self.MockHardwarepackHandler))
+ field_to_test = 'partition_layout'
+ data_to_set = 'bootfs_bogus_rootfs'
+ self.MockHardwarepackHandler.metadata_dict = {
+ field_to_test: data_to_set,
+ }
+ class config(BoardConfig):
+ pass
+ self.assertRaises(AssertionError, config.set_metadata, 'ahwpack.tar.gz')
+
+
class TestGetMLOFile(TestCaseWithFixtures):
def test_mlo_from_new_xloader(self):
@@ -150,6 +513,309 @@
AssertionError, _get_mlo_file, tempdir)
+class TestGetSMDKSPL(TestCaseWithFixtures):
+
+ def test_no_file_present(self):
+ tempdir = self.useFixture(CreateTempDirFixture()).get_temp_dir()
+ uboot_flavor = "some_uboot_flavour"
+ self.assertRaises(
+ AssertionError, boards.SMDKV310Config._get_smdk_spl, tempdir,
+ uboot_flavor)
+
+ def test_old_file_present(self):
+ tempdir = self.useFixture(CreateTempDirFixture()).get_temp_dir()
+ uboot_flavor = "some_uboot_flavour"
+ path = os.path.join(tempdir, 'usr', 'lib', 'u-boot', uboot_flavor)
+ os.makedirs(path)
+ spl_path = os.path.join(path, 'v310_mmc_spl.bin')
+ open(spl_path, 'w').close()
+ self.assertEquals(spl_path, boards.SMDKV310Config._get_smdk_spl(
+ tempdir, uboot_flavor))
+
+ def test_new_file_present(self):
+ tempdir = self.useFixture(CreateTempDirFixture()).get_temp_dir()
+ uboot_flavor = "some_uboot_flavour"
+ path = os.path.join(tempdir, 'usr', 'lib', 'u-boot', uboot_flavor)
+ os.makedirs(path)
+ spl_path = os.path.join(path, 'u-boot-mmc-spl.bin')
+ open(spl_path, 'w').close()
+ self.assertEquals(spl_path, boards.SMDKV310Config._get_smdk_spl(
+ tempdir, uboot_flavor))
+
+ def test_prefers_old_path(self):
+ tempdir = self.useFixture(CreateTempDirFixture()).get_temp_dir()
+ uboot_flavor = "some_uboot_flavour"
+ path = os.path.join(tempdir, 'usr', 'lib', 'u-boot', uboot_flavor)
+ os.makedirs(path)
+ old_spl_path = os.path.join(path, 'v310_mmc_spl.bin')
+ new_spl_path = os.path.join(path, 'u-boot-mmc-spl.bin')
+ open(old_spl_path, 'w').close()
+ open(new_spl_path, 'w').close()
+ self.assertEquals(old_spl_path, boards.SMDKV310Config._get_smdk_spl(
+ tempdir, uboot_flavor))
+
+
+class TestGetSMDKUboot(TestCaseWithFixtures):
+
+ def test_uses_uboot_flavour(self):
+ chroot_dir = "chroot"
+ uboot_flavor = "some_uboot_flavour"
+ uboot_file = os.path.join(chroot_dir, 'usr', 'lib', 'u-boot', uboot_flavor,
+ 'u-boot.bin')
+ self.assertEquals(uboot_file, boards.SMDKV310Config._get_smdk_uboot(
+ chroot_dir, uboot_flavor))
+
+
+class TestCreateToc(TestCaseWithFixtures):
+ ''' Tests boards.SnowballEmmcConfig.create_toc()'''
+
+ def setUp(self):
+ ''' Create a temporary directory to work in'''
+ super(TestCreateToc, self).setUp()
+ self.tempdir = self.useFixture(CreateTempDirFixture()).get_temp_dir()
+ #Create the test's input data structures
+ zero = '\x00\x00\x00\x00'
+ line1 = zero + zero + zero + zero + zero + 'b' + zero + zero + \
+ '\x00\x00\x00'
+ maxint = '\xFF\xFF\xFF\x7F'
+ minint = '\xFF\xFF\xFF\xFF'
+ line2 = maxint + maxint + zero + minint + minint + \
+ 'hello' + zero + '\x00\x00\x00'
+ line3 = '\x01\x00\x00\x00' '\x64\x00\x00\x00' + zero + \
+ '\x05\x00\x00\x00' '\x05\x00\x00\x00' \
+ 'hello' + zero + '\x00\x00\x00'
+ self.expected = line1 + line2 + line3
+
+ def create_files_structure(self, src_data):
+ ''' Creates the data structure that the tested function
+ needs as input'''
+ files = []
+ for line in src_data:
+ files.append({'section_name': line[5],
+ 'filename': 'N/A',
+ 'align': line[3],
+ 'offset': line[0],
+ 'size': line[1],
+ 'load_adress': 'N/A'})
+ return files
+
+ def test_create_toc_normal_case(self):
+ ''' Creates a toc file, and then reads the created
+ file and compares it to precomputed data'''
+ correct_data = [(0, 0, 0, 0, 0, 'b'),
+ (0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, -1, -1, 'hello'),
+ (1, 100, 1000, 5, 10, 'hello')]
+ files = self.create_files_structure(correct_data)
+ filename = os.path.join(self.tempdir, 'toc')
+ with open(filename, 'w') as f:
+ boards.SnowballEmmcConfig.create_toc(f, files)
+ with open(filename, 'r') as f:
+ actual = f.read()
+ self.assertEquals(96, len(actual))
+ for i in range(len(actual)):
+ self.assertEquals(self.expected[i], actual[i], 'Mismatch at ix' \
+ ' %d, ref=%c, actual=%c' % (i, self.expected[i], actual[i]))
+
+ def test_create_toc_error_too_large_section_name(self):
+ '''Verify that trying to write past the end of the
+ section name field raises an exception'''
+ illegal_name_data = [(0, 0, 0, 0, 0, 'Too_longName')]
+ files = self.create_files_structure(illegal_name_data)
+ with open(os.path.join(self.tempdir, 'toc'), 'w') as f:
+ self.assertRaises(AssertionError,
+ boards.SnowballEmmcConfig.create_toc,
+ f, files)
+
+ def test_create_toc_error_negative_unsigned(self):
+ '''Verify that trying to write a negative number to an unsigned
+ field raises an exception'''
+ illegal_unsigned_data = [(-3, 0, 0, 0, 0, 'xxx')]
+ files = self.create_files_structure(illegal_unsigned_data)
+ with open(os.path.join(self.tempdir, 'toc'), 'w') as f:
+ self.assertRaises(struct.error,
+ boards.SnowballEmmcConfig.create_toc,
+ f, files)
+
+
+class TestSnowballBootFiles(TestCaseWithFixtures):
+ ''' Tests boards.SnowballEmmcConfig.install_snowball_boot_loader()'''
+ ''' Tests boards.SnowballEmmcConfig._make_boot_files()'''
+ ''' Tests boards.SnowballEmmcConfig.get_file_info()'''
+
+ def setUp(self):
+ ''' Create temporary directory to work in'''
+ super(TestSnowballBootFiles, self).setUp()
+ self.tempdir = self.useFixture(CreateTempDirFixture()).get_temp_dir()
+ self.temp_bootdir_path = os.path.join(self.tempdir, 'boot')
+ if not os.path.exists(self.temp_bootdir_path):
+ os.makedirs(self.temp_bootdir_path)
+
+ def setupFiles(self):
+ ''' Adds some files in the temp dir that the tested function
+ can use as input:
+ * A config file, which the tested function reads to
+ discover which binary files should be written to
+ the loader partition.
+ * Test versions of the binary files themselves,
+ containing dummy data.
+ Returns the expected value that the tested function should
+ return, given these input files. '''
+ src_data = [('ISSW', 'boot_image_issw.bin', -1, 0, '5'),
+ ('X-LOADER', 'boot_image_x-loader.bin', -1, 0, '6'),
+ ('MEM_INIT', 'mem_init.bin', 0, 0x160000, '7'),
+ ('PWR_MGT', 'power_management.bin', 0, 0x170000, '8'),
+ ('NORMAL', 'u-boot.bin', 0, 0xBA0000, '9'),
+ ('UBOOT_ENV', 'u-boot-env.bin', 0, 0x00C1F000, '10')]
+ # Create a config file
+ cfg_file = os.path.join(self.temp_bootdir_path,
+ boards.SnowballEmmcConfig.SNOWBALL_STARTUP_FILES_CONFIG)
+ with open(cfg_file, 'w') as f:
+ for line in src_data:
+ # Write comments, so we test that the parser can read them
+ f.write('#Yet another comment\n')
+ f.write('%s %s %i %#x %s\n' % line)
+ expected = []
+ # Define dummy binary files, containing nothing but their own
+ # section names.
+ for line in src_data:
+ with open(os.path.join(self.temp_bootdir_path, line[1]), 'w') as f:
+ f.write(line[0])
+ #define the expected values read from the config file
+ expected = []
+ ofs = [boards.SnowballEmmcConfig.TOC_SIZE,
+ boards.SnowballEmmcConfig.TOC_SIZE + len('ISSW'), 0x160000,
+ 0x170000, 0xBA0000, 0xC1F000]
+ size = [len('ISSW'), len('X-LOADER'), len('MEM_INIT'), \
+ len('PWR_MGT'), len('NORMAL'), len('UBOOT_ENV')]
+ i = 0
+ for line in src_data:
+ filename = os.path.join(self.temp_bootdir_path, line[1])
+ expected.append({'section_name': line[0],
+ 'filename': filename,
+ 'align': int(line[2]),
+ 'offset': ofs[i],
+ 'size': long(size[i]),
+ 'load_adress': line[4]})
+ i += 1
+ return expected
+
+ def test_file_name_size(self):
+ ''' Test using a to large toc file '''
+ _, toc_filename = tempfile.mkstemp()
+ atexit.register(os.unlink, toc_filename)
+ filedata = 'X'
+ bytes = boards.SnowballEmmcConfig.TOC_SIZE + 1
+ tmpfile = open(toc_filename, 'wb')
+ for n in xrange(bytes):
+ tmpfile.write(filedata)
+ tmpfile.close()
+ files = self.setupFiles()
+ self.assertRaises(AssertionError,
+ boards.SnowballEmmcConfig.install_snowball_boot_loader,
+ toc_filename, files, "boot_device_or_file",
+ boards.SnowballEmmcConfig.SNOWBALL_LOADER_START_S)
+
+ def test_install_snowball_boot_loader_toc(self):
+ fixture = self.useFixture(MockCmdRunnerPopenFixture())
+ toc_filename = self.createTempFileAsFixture()
+ files = self.setupFiles()
+ boards.SnowballEmmcConfig.install_snowball_boot_loader(toc_filename,
+ files, "boot_device_or_file",
+ boards.SnowballEmmcConfig.SNOWBALL_LOADER_START_S)
+ expected = [
+ '%s dd if=%s of=boot_device_or_file bs=512 conv=notrunc' \
+ ' seek=%s' % (sudo_args, toc_filename,
+ boards.SnowballEmmcConfig.SNOWBALL_LOADER_START_S),
+ '%s dd if=%s/boot_image_issw.bin of=boot_device_or_file bs=512' \
+ ' conv=notrunc seek=257' % (sudo_args, self.temp_bootdir_path),
+ '%s rm %s/boot_image_issw.bin' % (sudo_args,
+ self.temp_bootdir_path),
+ '%s dd if=%s/boot_image_x-loader.bin of=boot_device_or_file' \
+ ' bs=1 conv=notrunc seek=131588'
+ % (sudo_args, self.temp_bootdir_path),
+ '%s rm %s/boot_image_x-loader.bin' % (sudo_args,
+ self.temp_bootdir_path),
+ '%s dd if=%s/mem_init.bin of=boot_device_or_file bs=512' \
+ ' conv=notrunc seek=3072' % (sudo_args, self.temp_bootdir_path),
+ '%s rm %s/mem_init.bin' % (sudo_args, self.temp_bootdir_path),
+ '%s dd if=%s/power_management.bin of=boot_device_or_file bs=512' \
+ ' conv=notrunc seek=3200' % (sudo_args, self.temp_bootdir_path),
+ '%s rm %s/power_management.bin' % (sudo_args,
+ self.temp_bootdir_path),
+ '%s dd if=%s/u-boot.bin of=boot_device_or_file bs=512' \
+ ' conv=notrunc seek=24064' % (sudo_args, self.temp_bootdir_path),
+ '%s rm %s/u-boot.bin' % (sudo_args, self.temp_bootdir_path),
+ '%s dd if=%s/u-boot-env.bin of=boot_device_or_file bs=512'
+ ' conv=notrunc seek=25080' % (sudo_args, self.temp_bootdir_path),
+ '%s rm %s/u-boot-env.bin' % (sudo_args, self.temp_bootdir_path)]
+
+ self.assertEqual(expected, fixture.mock.commands_executed)
+
+ def test_snowball_make_boot_files(self):
+ fixture = self.useFixture(MockCmdRunnerPopenFixture())
+ self.useFixture(MockSomethingFixture(tempfile, 'mkstemp',
+ lambda: (-1, '/tmp/temp_snowball_make_boot_files')))
+ self.setupFiles()
+ k_img_file = os.path.join(self.tempdir, 'vmlinuz-1-ux500')
+ i_img_file = os.path.join(self.tempdir, 'initrd.img-1-ux500')
+
+ boot_env = board_configs['snowball_emmc']._get_boot_env(
+ is_live=False, is_lowmem=False, consoles=[],
+ rootfs_uuid="test_boot_env_uuid", d_img_data=None)
+ boards.SnowballEmmcConfig._make_boot_files(boot_env, self.tempdir,
+ self.temp_bootdir_path, 'boot_device_or_file', k_img_file,
+ i_img_file, None)
+ expected = [
+ '%s mkimage -A arm -O linux -T kernel -C none -a 0x00008000 -e' \
+ ' 0x00008000 -n Linux -d %s %s/boot/uImage' % (sudo_args,
+ k_img_file, self.tempdir),
+ '%s cp /tmp/temp_snowball_make_boot_files %s/boot/boot.txt'
+ % (sudo_args, self.tempdir),
+ '%s mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n boot' \
+ ' script -d %s/boot/boot.txt %s/boot/flash.scr'
+ % (sudo_args, self.tempdir, self.tempdir),
+ '%s dd if=/tmp/temp_snowball_make_boot_files' \
+ ' of=boot_device_or_file bs=512 conv=notrunc seek=256'
+ % (sudo_args),
+ '%s dd if=%s/boot/boot_image_issw.bin of=boot_device_or_file' \
+ ' bs=512 conv=notrunc seek=257' % (sudo_args, self.tempdir),
+ '%s rm %s/boot_image_issw.bin' % (sudo_args,
+ self.temp_bootdir_path),
+ '%s dd if=%s/boot/boot_image_x-loader.bin of=boot_device_or_file' \
+ ' bs=1 conv=notrunc seek=131588' % (sudo_args, self.tempdir),
+ '%s rm %s/boot_image_x-loader.bin' % (sudo_args,
+ self.temp_bootdir_path),
+ '%s dd if=%s/boot/mem_init.bin of=boot_device_or_file bs=512' \
+ ' conv=notrunc seek=3072' % (sudo_args, self.tempdir),
+ '%s rm %s/mem_init.bin' % (sudo_args, self.temp_bootdir_path),
+ '%s dd if=%s/boot/power_management.bin of=boot_device_or_file' \
+ ' bs=512 conv=notrunc seek=3200' % (sudo_args, self.tempdir),
+ '%s rm %s/power_management.bin' % (sudo_args,
+ self.temp_bootdir_path),
+ '%s dd if=%s/boot/u-boot.bin of=boot_device_or_file bs=512' \
+ ' conv=notrunc seek=24064' % (sudo_args, self.tempdir),
+ '%s rm %s/u-boot.bin' % (sudo_args, self.temp_bootdir_path),
+ '%s dd if=%s/boot/u-boot-env.bin of=boot_device_or_file bs=512' \
+ ' conv=notrunc seek=25080' % (sudo_args, self.tempdir),
+ '%s rm %s/u-boot-env.bin' % (sudo_args, self.temp_bootdir_path),
+ '%s rm /tmp/temp_snowball_make_boot_files' % (sudo_args),
+ '%s rm %s/startfiles.cfg' % (sudo_args, self.temp_bootdir_path)]
+
+ self.assertEqual(expected, fixture.mock.commands_executed)
+
+ def test_missing_files(self):
+ '''When the files cannot be read, an IOError should be raised'''
+ self.assertRaises(IOError,
+ boards.SnowballEmmcConfig.get_file_info,
+ self.tempdir)
+
+ def test_normal_case(self):
+ expected = self.setupFiles()
+ actual = boards.SnowballEmmcConfig.get_file_info(
+ self.temp_bootdir_path)
+ self.assertEquals(expected, actual)
+
+
class TestBootSteps(TestCaseWithFixtures):
def setUp(self):
@@ -199,6 +865,10 @@
def test_mx5_steps(self):
class SomeMx5Config(boards.Mx5Config):
uboot_flavor = 'uboot_flavor'
+ SomeMx5Config.hardwarepack_handler = (
+ TestSetMetadata.MockHardwarepackHandler('ahwpack.tar.gz'))
+ SomeMx5Config.hardwarepack_handler.get_format = (
+ lambda: '1.0')
self.make_boot_files(SomeMx5Config)
expected = [
'install_mx5_boot_loader', 'make_uImage', 'make_uInitrd',
@@ -206,9 +876,21 @@
self.assertEqual(expected, self.funcs_calls)
def test_smdkv310_steps(self):
+ def mock_func_creator(name):
+ return classmethod(
+ lambda *args, **kwargs: self.funcs_calls.append(name))
+
+ self.useFixture(MockSomethingFixture(
+ linaro_image_tools.media_create.boards.SMDKV310Config,
+ 'install_smdk_boot_loader',
+ mock_func_creator('install_smdk_boot_loader')))
+ boards.SMDKV310Config.hardwarepack_handler = (
+ TestSetMetadata.MockHardwarepackHandler('ahwpack.tar.gz'))
+ boards.SMDKV310Config.hardwarepack_handler.get_format = (
+ lambda: '1.0')
self.make_boot_files(boards.SMDKV310Config)
expected = [
- '_dd', '_dd', 'make_flashable_env', '_dd', 'make_uImage',
+ 'install_smdk_boot_loader', 'make_flashable_env', '_dd', 'make_uImage',
'make_uInitrd', 'make_boot_script']
self.assertEqual(expected, self.funcs_calls)
@@ -354,6 +1036,18 @@
'1,8191,0xDA\n8192,106496,0x0C,*\n114688,,,-',
board_configs['smdkv310'].get_sfdisk_cmd())
+ def test_panda_android(self):
+ self.assertEqual(
+ '63,270272,0x0C,*\n270336,524288,L\n794624,524288,L\n' \
+ '1318912,-,E\n1318912,1048576,L\n2367488,,,-',
+ android_boards.AndroidPandaConfig.get_sfdisk_cmd())
+
+ def test_snowball_emmc_android(self):
+ self.assertEqual(
+ '256,7936,0xDA\n8192,262144,0x0C,*\n270336,524288,L\n' \
+ '794624,-,E\n794624,524288,L\n1318912,1048576,L\n2367488,,,-',
+ android_boards.AndroidSnowballEmmcConfig.get_sfdisk_cmd())
+
class TestGetBootCmd(TestCase):
@@ -509,6 +1203,41 @@
self.assertEqual(expected, boot_commands)
+class TestGetBootCmdAndroid(TestCase):
+ def test_panda(self):
+ # XXX: To fix bug 697824 we have to change class attributes of our
+ # OMAP board configs, and some tests do that so to make sure they
+ # don't interfere with us we'll reset that before doing anything.
+ config = android_board_configs['panda']
+ config.serial_tty = config._serial_tty
+ boot_commands = config._get_boot_env(consoles=[])
+ expected = {
+ 'bootargs': 'console=tty0 console=ttyO2,115200n8 '
+ 'rootwait ro earlyprintk fixrtc '
+ 'nocompcache vram=48M omapfb.vram=0:24M,1:24M '
+ 'mem=456M@0x80000000 mem=512M@0xA0000000 '
+ 'init=/init androidboot.console=ttyO2',
+ 'bootcmd': 'fatload mmc 0:1 0x80200000 uImage; '
+ 'fatload mmc 0:1 0x81600000 uInitrd; '
+ 'bootm 0x80200000 0x81600000'}
+ self.assertEqual(expected, boot_commands)
+
+ def test_android_snowball_emmc(self):
+ boot_commands = (android_boards.AndroidSnowballEmmcConfig.
+ _get_boot_env(consoles=[]))
+ expected = {
+ 'bootargs': 'console=tty0 console=ttyAMA2,115200n8 '
+ 'rootwait ro earlyprintk '
+ 'rootdelay=1 fixrtc nocompcache '
+ 'mem=128M@0 mali.mali_mem=64M@128M mem=24M@192M '
+ 'hwmem=167M@216M mem_issw=1M@383M mem=640M@384M '
+ 'vmalloc=256M init=/init androidboot.console=ttyAMA2',
+ 'bootcmd': 'fatload mmc 1:1 0x00100000 uImage; '
+ 'fatload mmc 1:1 0x08000000 uInitrd; '
+ 'bootm 0x00100000 0x08000000'}
+ self.assertEqual(expected, boot_commands)
+
+
class TestUnpackBinaryTarball(TestCaseWithFixtures):
def setUp(self):
@@ -607,7 +1336,8 @@
def test_install_mx5_boot_loader(self):
fixture = self._mock_Popen()
imx_file = self.createTempFileAsFixture()
- install_mx5_boot_loader(imx_file, "boot_device_or_file")
+ install_mx5_boot_loader(imx_file, "boot_device_or_file",
+ BoardConfig.LOADER_MIN_SIZE_S)
expected = [
'%s dd if=%s of=boot_device_or_file bs=512 '
'conv=notrunc seek=2' % (sudo_args, imx_file)]
@@ -616,9 +1346,10 @@
def test_install_mx5_boot_loader_too_large(self):
self.useFixture(MockSomethingFixture(
os.path, "getsize",
- lambda s: (LOADER_MIN_SIZE_S - 1) * SECTOR_SIZE + 1))
+ lambda s: (BoardConfig.LOADER_MIN_SIZE_S - 1) * SECTOR_SIZE + 1))
self.assertRaises(AssertionError,
- install_mx5_boot_loader, "imx_file", "boot_device_or_file")
+ install_mx5_boot_loader, "imx_file", "boot_device_or_file",
+ BoardConfig.LOADER_MIN_SIZE_S)
def test_install_omap_boot_loader(self):
fixture = self._mock_Popen()
@@ -630,6 +1361,32 @@
'%s cp -v chroot_dir/MLO boot_disk' % sudo_args, 'sync']
self.assertEqual(expected, fixture.mock.commands_executed)
+ def test_install_smdk_u_boot(self):
+ fixture = self._mock_Popen()
+ uboot_flavor = "some_u_boot_flavour"
+ self.useFixture(MockSomethingFixture(
+ boards.SMDKV310Config, '_get_smdk_spl',
+ classmethod(lambda cls, chroot_dir, uboot_flavor: "%s/%s/SPL" % (
+ chroot_dir, uboot_flavor))))
+ self.useFixture(MockSomethingFixture(
+ boards.SMDKV310Config, '_get_smdk_uboot',
+ classmethod(lambda cls, chroot_dir, uboot_flavor: "%s/%s/uboot" % (
+ chroot_dir, uboot_flavor))))
+ boards.SMDKV310Config.hardwarepack_handler = (
+ TestSetMetadata.MockHardwarepackHandler('ahwpack.tar.gz'))
+ boards.SMDKV310Config.hardwarepack_handler.get_format = (
+ lambda: '1.0')
+ self.useFixture(MockSomethingFixture(os.path, 'getsize',
+ lambda file: 1))
+ boards.SMDKV310Config.install_smdk_boot_loader(
+ "chroot_dir", "boot_disk", uboot_flavor)
+ expected = [
+ '%s dd if=chroot_dir/%s/SPL of=boot_disk bs=512 conv=notrunc '
+ 'seek=%d' % (sudo_args, uboot_flavor, SAMSUNG_V310_BL1_START),
+ '%s dd if=chroot_dir/%s/uboot of=boot_disk bs=512 conv=notrunc '
+ 'seek=%d' % (sudo_args, uboot_flavor, SAMSUNG_V310_BL2_START)]
+ self.assertEqual(expected, fixture.mock.commands_executed)
+
def test_get_plain_boot_script_contents(self):
boot_env = {'bootargs': 'mybootargs', 'bootcmd': 'mybootcmd'}
boot_script_data = get_plain_boot_script_contents(boot_env)
@@ -893,13 +1650,21 @@
(63 * SECTOR_SIZE, 32768 * SECTOR_SIZE),
(32831 * SECTOR_SIZE, 65536 * SECTOR_SIZE),
(98367 * SECTOR_SIZE, 65536 * SECTOR_SIZE),
- (294975 * SECTOR_SIZE, (self.android_image_size -
- 294975 * SECTOR_SIZE)),
((294975 + ext_part_size) * SECTOR_SIZE,
(131072 - ext_part_size) * SECTOR_SIZE),
((426047 + ext_part_size) * SECTOR_SIZE,
self.android_image_size - (426047 + ext_part_size) * SECTOR_SIZE)
]
+
+ self.android_snowball_offsets_and_sizes = [
+ (8192 * SECTOR_SIZE, 24639 * SECTOR_SIZE),
+ (32831 * SECTOR_SIZE, 65536 * SECTOR_SIZE),
+ ((98367 + ext_part_size)* SECTOR_SIZE,
+ (65536 - ext_part_size) * SECTOR_SIZE),
+ (294975 * SECTOR_SIZE, 131072 * SECTOR_SIZE),
+ ((426047 + ext_part_size) * SECTOR_SIZE,
+ self.android_image_size - (426047 + ext_part_size) * SECTOR_SIZE)
+ ]
def tearDown(self):
super(TestPartitionSetup, self).tearDown()
@@ -916,6 +1681,13 @@
'63,32768,0x0C,*\n32831,65536,L\n98367,65536,L\n294975,-,E\n' \
'294975,131072,L\n426047,,,-', '%s' % self.android_image_size)
+ def _create_snowball_android_tmpfile(self):
+ # raw, boot, system, cache, (extended), userdata and sdcard partitions
+ return self._create_qemu_img_with_partitions(
+ '256,7936,0xDA\n8192,24639,0x0C,*\n32831,65536,L\n' \
+ '98367,-,E\n98367,65536,L\n294975,131072,L\n' \
+ '426047,,,-', '%s' % self.android_image_size)
+
def test_convert_size_no_suffix(self):
self.assertEqual(524288, convert_size_to_bytes('524288'))
@@ -945,6 +1717,15 @@
self.android_offsets_and_sizes):
self.assertEqual(device_pair, expected_pair)
+ def test_calculate_snowball_android_partition_size_and_offset(self):
+ tmpfile = self._create_snowball_android_tmpfile()
+ device_info = calculate_android_partition_size_and_offset(tmpfile)
+ # We use map(None, ...) since it would catch if the lists are not of
+ # equal length and zip() would not in all cases.
+ for device_pair, expected_pair in map(None, device_info,
+ self.android_snowball_offsets_and_sizes):
+ self.assertEqual(device_pair, expected_pair)
+
def test_partition_numbering(self):
# another Linux partition at +24 MiB after the boot/root parts
tmpfile = self._create_qemu_img_with_partitions(
@@ -1054,7 +1835,7 @@
# get_boot_and_root_loopback_devices will also setup two exit handlers
# to de-register the loopback devices set up above.
- self.assertEqual(6, len(atexit_fixture.mock.funcs))
+ self.assertEqual(5, len(atexit_fixture.mock.funcs))
popen_fixture.mock.calls = []
atexit_fixture.mock.run_funcs()
# We did not really run losetup above (as it requires root) so here we
@@ -1065,7 +1846,6 @@
'%s losetup -d ' % sudo_args,
'%s losetup -d ' % sudo_args,
'%s losetup -d ' % sudo_args,
- '%s losetup -d ' % sudo_args,
'%s losetup -d ' % sudo_args],
popen_fixture.mock.commands_executed)
@@ -1159,6 +1939,9 @@
self.config = c
self.config.boot_script = 'boot_script'
+ self.config.hardwarepack_handler = \
+ TestSetMetadata.MockHardwarepackHandler('ahwpack.tar.gz')
+ self.config.hardwarepack_handler.get_format = lambda: '1.0'
self.popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
self.useFixture(MockSomethingFixture(
self.config, 'make_boot_files', self.save_args))
=== modified file 'linaro_image_tools/tests/fixtures.py'
@@ -20,6 +20,7 @@
import os
import shutil
import tempfile
+from StringIO import StringIO
from linaro_image_tools import cmd_runner
@@ -68,8 +69,11 @@
# used in tests to make sure all callsites wait for their child.
child_finished = True
+ def __init__(self, assert_child_finished=True):
+ self.assert_child_finished = assert_child_finished
+
def __call__(self, cmd, *args, **kwargs):
- if not self.child_finished:
+ if self.assert_child_finished and not self.child_finished:
raise AssertionError(
"You should call wait() or communicate() to ensure "
"the subprocess is finished before proceeding.")
@@ -97,6 +101,9 @@
def commands_executed(self):
return [' '.join(args) for args in self.calls]
+ @property
+ def stdin(self):
+ return StringIO()
class MockCmdRunnerPopenFixture(MockSomethingFixture):
"""A test fixture which mocks cmd_runner.do_run with the given mock.
@@ -104,13 +111,13 @@
If no mock is given, a MockCmdRunnerPopen instance is used.
"""
- def __init__(self):
+ def __init__(self, assert_child_finished=True):
super(MockCmdRunnerPopenFixture, self).__init__(
- cmd_runner, 'Popen', MockCmdRunnerPopen())
+ cmd_runner, 'Popen', MockCmdRunnerPopen(assert_child_finished))
def tearDown(self):
super(MockCmdRunnerPopenFixture, self).tearDown()
- if not self.mock.child_finished:
+ if self.mock.assert_child_finished and not self.mock.child_finished:
raise AssertionError(
"You should call wait() or communicate() to ensure "
"the subprocess is finished before proceeding.")
=== modified file 'linaro_image_tools/tests/test_pyflakes.py'
@@ -29,8 +29,8 @@
(stdout, stderr) = proc.communicate()
stdout = stdout.splitlines()
stdout.sort()
- expected = ["./linaro_image_tools/utils.py:26: redefinition of "
- "unused 'CommandNotFound' from line 24" ]
+ expected = ["./linaro_image_tools/utils.py:27: redefinition of "
+ "unused 'CommandNotFound' from line 25" ]
self.assertEquals(expected, stdout)
self.assertEquals('', stderr)
=== modified file 'linaro_image_tools/tests/test_utils.py'
@@ -35,12 +35,49 @@
install_package_providing,
preferred_tools_dir,
UnableToFindPackageProvidingCommand,
+ verify_file_integrity,
)
sudo_args = " ".join(cmd_runner.SUDO_ARGS)
+class TestVerifyFileIntegrity(TestCaseWithFixtures):
+
+ filenames_in_shafile = ['verified-file1', 'verified-file2']
+
+ class MockCmdRunnerPopen(object):
+ def __call__(self, cmd, *args, **kwargs):
+ self.returncode = 0
+ return self
+
+ def communicate(self, input=None):
+ self.wait()
+ return ': OK\n'.join(
+ TestVerifyFileIntegrity.filenames_in_shafile) + ': OK\n', ''
+
+ def wait(self):
+ return self.returncode
+
+ def test_verify_files(self):
+ fixture = self.useFixture(MockCmdRunnerPopenFixture())
+ hash_filename = "dummy-file.txt"
+ signature_filename = hash_filename + ".asc"
+ verify_file_integrity([signature_filename])
+ self.assertEqual(
+ ['gpg --verify %s' % signature_filename,
+ 'sha1sum -c %s' % hash_filename],
+ fixture.mock.commands_executed)
+
+ def test_verify_files_returns_files(self):
+ self.useFixture(MockSomethingFixture(cmd_runner, 'Popen',
+ self.MockCmdRunnerPopen()))
+ hash_filename = "dummy-file.txt"
+ signature_filename = hash_filename + ".asc"
+ verified_files = verify_file_integrity([signature_filename])
+ self.assertEqual(self.filenames_in_shafile, verified_files)
+
+
class TestEnsureCommand(TestCaseWithFixtures):
install_pkg_providing_called = False
=== modified file 'linaro_image_tools/utils.py'
@@ -19,6 +19,7 @@
import os
import platform
+import subprocess
try:
from CommandNotFound import CommandNotFound
@@ -28,6 +29,31 @@
from linaro_image_tools import cmd_runner
+def verify_file_integrity(sig_file_list):
+ """Verify a list of signature files.
+
+ The parameter is a list of filenames of gpg signature files which will be
+ verified using gpg. For each of the files it is assumed that there is an
+ sha1 hash file with the same file name minus the '.asc' extension.
+
+ Each of the sha1 files will be checked using sha1sums. All files listed in
+ the sha1 hash file must be found in the same directory as the hash file.
+ """
+ verified_files = []
+ for sig_file in sig_file_list:
+ hash_file = sig_file[0:-len('.asc')]
+ cmd_runner.run(['gpg', '--verify', sig_file]).wait()
+ if os.path.dirname(hash_file) == '':
+ sha_cwd = None
+ else:
+ sha_cwd = os.path.dirname(hash_file)
+ sha1sums_out, _ = cmd_runner.run(['sha1sum', '-c', hash_file],
+ stdout=subprocess.PIPE, cwd=sha_cwd
+ ).communicate()
+ verified_files.extend(sha1sums_out.replace(': OK', '').splitlines())
+ return verified_files
+
+
def install_package_providing(command):
"""Install a package which provides the given command.
=== modified file 'setup.py'
@@ -5,7 +5,7 @@
DistUtilsExtra.auto.setup(
name="linaro-image-tools",
- version="0.4.8.1",
+ version="2011.06-1.1",
description="Tools to create and write Linaro images",
url="https://launchpad.net/linaro-image-tools",
license="GPL v3 or later",