1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-02-20 10:54:14 +01:00

Merge branch 'OP-483_os_release-packaging'

This commit is contained in:
Oleg Semyonov 2011-05-18 01:06:11 +03:00
commit 68ac5a271c
3 changed files with 373 additions and 4 deletions

View File

@ -0,0 +1,299 @@
#!/usr/bin/env python
#
# Utility functions to access git repository info and
# generate source files and binary objects using templates.
#
# (c) 2011, The OpenPilot Team, http://www.openpilot.org
# See also: The GNU Public License (GPL) Version 3
#
from subprocess import Popen, PIPE
from re import search, MULTILINE
from datetime import datetime
from string import Template
import argparse
import hashlib
import sys
class Repo:
"""A simple git repository HEAD commit info class
This simple class provides object notation to access
the git repository current commit info. If one needs
better access one can try the GitPython class available
here:
http://packages.python.org/GitPython
It is not installed by default, so we cannot rely on it.
Example:
r = Repo('/path/to/git/repository')
print "path: ", r.path()
print "origin: ", r.origin()
print "hash: ", r.hash()
print "short hash: ", r.hash(8)
print "Unix time: ", r.time()
print "commit date:", r.time("%Y%m%d")
print "commit tag: ", r.tag()
print "branch: ", r.branch()
print "release tag:", r.reltag()
"""
def _exec(self, cmd):
"""Execute git using cmd as arguments"""
self._git = 'git'
git = Popen(self._git + " " + cmd, cwd=self._path,
shell=True, stdout=PIPE, stderr=PIPE)
self._out, self._err = git.communicate()
self._rc = git.poll()
def _get_origin(self):
"""Get and store the repository origin URL"""
self._origin = None
self._exec('remote -v')
if self._rc == 0:
m = search(r"^origin\s+(.+)\s+\(fetch\)$", self._out, MULTILINE)
if m:
self._origin = m.group(1)
def _get_time(self):
"""Get and store HEAD commit timestamp in Unix format
We use commit timestamp rather than the build time,
so it always is the same for the current commit or tag.
"""
self._time = None
self._exec('log -n1 --no-color --format=format:%ct HEAD')
if self._rc == 0:
self._time = self._out
def _get_tag(self):
"""Get and store git tag for the HEAD commit"""
self._tag = None
self._exec('describe --exact-match HEAD')
if self._rc == 0:
self._tag = self._out.strip(' \t\n\r')
def _get_branch(self):
"""Get and store current branch containing the HEAD commit"""
self._branch = None
self._exec('branch --contains HEAD')
if self._rc == 0:
m = search(r"^\*\s+(.+)$", self._out, MULTILINE)
if m:
self._branch = m.group(1)
def __init__(self, path = "."):
"""Initialize object instance and read repo info"""
self._path = path
self._exec('rev-parse --verify HEAD')
if self._rc == 0:
self._hash = self._out.strip(' \t\n\r')
self._get_origin()
self._get_time()
self._get_tag()
self._get_branch()
else:
self._hash = None
self._origin = None
self._time = None
self._tag = None
self._branch = None
def path(self):
"""Return the repository path"""
return self._path
def origin(self, none = None):
"""Return fetch origin of the repository"""
if self._origin == None:
return none
else:
return self._origin
def hash(self, n = 40, none = None):
"""Return hash of the HEAD commit"""
if self._hash == None:
return none
else:
return self._hash[:n]
def time(self, format = None, none = None):
"""Return Unix or formatted time of the HEAD commit"""
if self._time == None:
return none
else:
if format == None:
return self._time
else:
return datetime.utcfromtimestamp(float(self._time)).strftime(format)
def tag(self, none = None):
"""Return git tag for the HEAD commit or given string if none"""
if self._tag == None:
return none
else:
return self._tag
def branch(self, none = None):
"""Return git branch containing the HEAD or given string if none"""
if self._branch == None:
return none
else:
return self._branch
def info(self):
"""Print some repository info"""
print "path: ", self.path()
print "origin: ", self.origin()
print "Unix time: ", self.time()
print "commit date:", self.time("%Y%m%d")
print "hash: ", self.hash()
print "short hash: ", self.hash(8)
print "branch: ", self.branch()
print "commit tag: ", self.tag()
def file_from_template(tpl_name, out_name, dict):
"""Create or update file from template using dictionary
This function reads the template, performs placeholder replacement
using the dictionary and checks if output file with such content
already exists. If no such file or file data is different from
expected then it will be ovewritten with new data. Otherwise it
will not be updated so make will not update dependent targets.
Example:
# template.c:
# char source[] = "${OUTFILENAME}";
# uint32_t timestamp = ${UNIXTIME};
# uint32_t hash = 0x${HASH8};
r = Repo('/path/to/git/repository')
tpl_name = "template.c"
out_name = "output.c"
dictionary = dict(
HASH8 = r.hash(8),
UNIXTIME = r.time(),
OUTFILENAME = out_name,
)
file_from_template(tpl_name, out_name, dictionary)
"""
# Read template first
tf = open(tpl_name, "rb")
tpl = tf.read()
tf.close()
# Replace placeholders using dictionary
out = Template(tpl).substitute(dict)
# Check if output file already exists
try:
of = open(out_name, "rb")
except IOError:
# No file - create new
of = open(out_name, "wb")
of.write(out)
of.close()
else:
# File exists - overwite only if content is different
inp = of.read()
of.close()
if inp != out:
of = open(out_name, "wb")
of.write(out)
of.close()
def sha1(file):
"""Provides C source representation of sha1 sum of file."""
if file == None:
return ""
else:
sha1 = hashlib.sha1()
with open(file, 'rb') as f:
for chunk in iter(lambda: f.read(8192), ''):
sha1.update(chunk)
hex_stream = lambda s:",".join(['0x'+hex(ord(c))[2:].zfill(2) for c in s])
return hex_stream(sha1.digest())
def main():
"""This utility uses git repository in the current working directory
or from the given path to extract some info about it and HEAD commit.
Then some variables in the form of ${VARIABLE} could be replaced by
collected data. Optional board type, board revision and sha1 sum
of given image file could be applied as well or will be replaced by
empty strings if not defined.
If --info option is given, some repository info will be printed to
stdout.
If --format option is given then utility prints the format string
after substitution to the standard output.
If --outfile option is given then the --template option should be
defined too. In that case the utility reads a template file, performs
variable substitution and writes the result into output file. Output
file will be overwritten only if its content differs from expected.
Otherwise it will not be touched, so make utility will not remake
dependent targets.
"""
# Parse command line.
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description = "Performs variable substitution in template file or string.",
epilog = main.__doc__);
parser.add_argument('--path', default='.',
help='path to the git repository');
parser.add_argument('--info', action='store_true',
help='print repository info to stdout');
parser.add_argument('--format',
help='format string to print to stdout');
parser.add_argument('--template',
help='name of template file');
parser.add_argument('--outfile',
help='name of output file');
parser.add_argument('--image',
help='name of image file for sha1 calculation');
parser.add_argument('--type', default="",
help='board type, for example, 0x04 for CopterControl');
parser.add_argument('--revision', default = "",
help='board revision, for example, 0x01');
args = parser.parse_args()
# Process arguments. No advanced error handling is here.
# Any error will raise an exception and terminate process
# with non-zero exit code.
r = Repo(args.path)
dictionary = dict(
TEMPLATE = args.template,
OUTFILENAME = args.outfile,
ORIGIN = r.origin(),
HASH = r.hash(),
HASH8 = r.hash(8),
TAG_OR_BRANCH = r.tag(r.branch('unreleased')),
TAG_OR_HASH8 = r.tag(r.hash(8, 'untagged')),
UNIXTIME = r.time(),
DATE = r.time('%Y%m%d'),
BOARD_TYPE = args.type,
BOARD_REVISION = args.revision,
SHA1 = sha1(args.image),
)
if args.info:
r.info()
if args.format != None:
print Template(args.format).substitute(dictionary)
if args.outfile != None:
file_from_template(args.template, args.outfile, dictionary)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,71 @@
/**
******************************************************************************
*
* @file ${OUTFILENAME}
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011.
* @brief Firmware version info file autogenerated using template
* ${TEMPLATE}
*
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program 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
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdint.h>
/**
* We have 100 bytes for the whole description.
*
* Only the first 40 are visible on the FirmwareIAP uavobject, the remaining
* 60 are ok to use for packaging and will be saved in the flash.
*
* Structure is:
* 4 bytes: header: "OpFw".
* 4 bytes: GIT commit tag (short version of SHA1).
* 4 bytes: Unix timestamp of compile time.
* 2 bytes: target platform. Should follow same rule as BOARD_TYPE and BOARD_REVISION in board define files.
* 26 bytes: commit tag if it is there, otherwise branch name. Zero-padded.
* ---- 40 bytes limit ---
* 20 bytes: SHA1 sum of the firmware.
* 40 bytes: free for now.
*
*/
struct __attribute__((packed)) fw_version_info {
uint8_t magic[4];
uint32_t commit_hash_prefix;
uint32_t timestamp;
uint8_t board_type;
uint8_t board_revision;
uint8_t commit_tag_name[26];
uint8_t sha1sum[20];
uint8_t pad[40];
};
const struct fw_version_info fw_version_blob __attribute__((used)) __attribute__((__section__(".fw_version_blob"))) = {
.magic = { 'O','p','F','w' },
.commit_hash_prefix = 0x${HASH8},
.timestamp = ${UNIXTIME},
.board_type = ${BOARD_TYPE},
.board_revision = ${BOARD_REVISION},
.commit_tag_name = "${TAG_OR_BRANCH}",
.sha1sum = { ${SHA1} },
};
/**
* @}
*/

View File

@ -13,9 +13,8 @@ ROOT_DIR := $(realpath $(WHEREAMI)/../)
# Set up some macros
BUILD_DIR := $(ROOT_DIR)/build
RELEASE_DATE := $(shell date +%Y%m%d)
RELEASE_TAG := unreleased
RELEASE_LBL := $(RELEASE_DATE)-$(RELEASE_TAG)
VERSION_CMD := python $(ROOT_DIR)/make/scripts/version-info.py --path=$(ROOT_DIR)
RELEASE_LBL := $(shell $(VERSION_CMD) --format=\$${DATE}-\$${TAG_OR_HASH8})
RELEASE_DIR := $(BUILD_DIR)/release-$(RELEASE_LBL)
FW_DIR := $(RELEASE_DIR)/firmware-$(RELEASE_LBL)
BL_DIR := $(FW_DIR)/bootloaders
@ -89,7 +88,7 @@ $(eval $(call INSTALL_TEMPLATE,blupd_ahrs,all_bl,$(BLUPD_DIR),AHRS_,-$(RELEASE_L
$(eval $(call INSTALL_TEMPLATE,blupd_openpilot,all_bl,$(BLUPD_DIR),OpenPilot_,-$(RELEASE_LBL),,,blupd_openpilot,install))
$(eval $(call INSTALL_TEMPLATE,blupd_pipxtreme,all_bl,$(BLUPD_DIR),PipXtreme_,-$(RELEASE_LBL),,,blupd_pipxtreme,install))
# CopterControl flash eraser tool (change fw_spektrum to fw_ppm if fw_ppm is enabled in release_fw target below)
# CopterControl flash eraser tool
$(eval $(call INSTALL_TEMPLATE,fw_tools,uavobjects,$(BLUPD_DIR),,-FlashEraser-$(RELEASE_LBL),ERASE_FLASH=YES,clean,$(FW_TARGETS_TOOLS),install))
# Order-only dependencies