2014-12-19 11:41:55 +00:00
# Development tool - standard commands plugin
#
2015-03-04 12:46:29 +00:00
# Copyright (C) 2014-2015 Intel Corporation
2014-12-19 11:41:55 +00:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2015-05-11 13:17:01 +00:00
""" Devtool standard plugins """
2014-12-19 11:41:55 +00:00
import os
import sys
import re
import shutil
import tempfile
import logging
import argparse
2015-04-28 11:25:30 +00:00
import scriptutils
2014-12-19 11:41:55 +00:00
from devtool import exec_build_env_command , setup_tinfoil
logger = logging . getLogger ( ' devtool ' )
def plugin_init ( pluginlist ) :
2015-05-11 13:17:01 +00:00
""" Plugin initialization """
2014-12-19 11:41:55 +00:00
pass
def add ( args , config , basepath , workspace ) :
2015-05-11 13:17:01 +00:00
""" Entry point for the devtool ' add ' subcommand """
2014-12-19 11:41:55 +00:00
import bb
import oe . recipeutils
if args . recipename in workspace :
logger . error ( " recipe %s is already in your workspace " % args . recipename )
return - 1
reason = oe . recipeutils . validate_pn ( args . recipename )
if reason :
logger . error ( reason )
return - 1
srctree = os . path . abspath ( args . srctree )
2015-04-28 10:21:55 +00:00
if os . path . exists ( srctree ) :
if args . fetch :
if not os . path . isdir ( srctree ) :
logger . error ( " Cannot fetch into source tree path %s as it exists and is not a directory " % srctree )
return 1
elif os . listdir ( srctree ) :
logger . error ( " Cannot fetch into source tree path %s as it already exists and is non-empty " % srctree )
return 1
else :
if not args . fetch :
logger . error ( " Specified source tree %s could not be found " % srctree )
return 1
2014-12-19 11:41:55 +00:00
appendpath = os . path . join ( config . workspace_path , ' appends ' )
if not os . path . exists ( appendpath ) :
os . makedirs ( appendpath )
recipedir = os . path . join ( config . workspace_path , ' recipes ' , args . recipename )
bb . utils . mkdirhier ( recipedir )
2015-04-28 16:07:31 +00:00
rfv = None
2014-12-19 11:41:55 +00:00
if args . version :
if ' _ ' in args . version or ' ' in args . version :
logger . error ( ' Invalid version string " %s " ' % args . version )
return - 1
2015-04-28 16:07:31 +00:00
rfv = args . version
if args . fetch :
if args . fetch . startswith ( ' git:// ' ) :
rfv = ' git '
elif args . fetch . startswith ( ' svn:// ' ) :
rfv = ' svn '
elif args . fetch . startswith ( ' hg:// ' ) :
rfv = ' hg '
if rfv :
bp = " %s _ %s " % ( args . recipename , rfv )
2014-12-19 11:41:55 +00:00
else :
bp = args . recipename
recipefile = os . path . join ( recipedir , " %s .bb " % bp )
if sys . stdout . isatty ( ) :
color = ' always '
else :
color = args . color
2015-04-28 10:21:55 +00:00
extracmdopts = ' '
if args . fetch :
source = args . fetch
extracmdopts = ' -x %s ' % srctree
else :
source = srctree
2015-04-28 16:07:31 +00:00
if args . version :
extracmdopts + = ' -V %s ' % args . version
2015-05-14 09:18:18 +00:00
try :
stdout , _ = exec_build_env_command ( config . init_path , basepath , ' recipetool --color= %s create -o %s " %s " %s ' % ( color , recipefile , source , extracmdopts ) )
logger . info ( ' Recipe %s has been automatically created; further editing may be required to make it fully functional ' % recipefile )
except bb . process . ExecutionError as e :
logger . error ( ' Command \' %s \' failed: \n %s ' % ( e . command , e . stdout ) )
return 1
2014-12-19 11:41:55 +00:00
_add_md5 ( config , args . recipename , recipefile )
initial_rev = None
if os . path . exists ( os . path . join ( srctree , ' .git ' ) ) :
( stdout , _ ) = bb . process . run ( ' git rev-parse HEAD ' , cwd = srctree )
initial_rev = stdout . rstrip ( )
2015-04-27 09:53:18 +00:00
appendfile = os . path . join ( appendpath , ' %s .bbappend ' % bp )
2014-12-19 11:41:55 +00:00
with open ( appendfile , ' w ' ) as f :
f . write ( ' inherit externalsrc \n ' )
f . write ( ' EXTERNALSRC = " %s " \n ' % srctree )
2015-02-19 16:39:56 +00:00
if args . same_dir :
f . write ( ' EXTERNALSRC_BUILD = " %s " \n ' % srctree )
2014-12-19 11:41:55 +00:00
if initial_rev :
f . write ( ' \n # initial_rev: %s \n ' % initial_rev )
_add_md5 ( config , args . recipename , appendfile )
return 0
2015-03-05 10:51:20 +00:00
def _check_compatible_recipe ( pn , d ) :
2015-05-11 13:17:01 +00:00
""" Check if the recipe is supported by devtool """
2015-03-05 10:51:20 +00:00
if pn == ' perf ' :
logger . error ( " The perf recipe does not actually check out source and thus cannot be supported by this tool " )
return False
2015-05-07 13:52:24 +00:00
if pn in [ ' kernel-devsrc ' , ' package-index ' ] or pn . startswith ( ' gcc-source ' ) :
2015-03-05 10:51:20 +00:00
logger . error ( " The %s recipe is not supported by this tool " % pn )
return False
if bb . data . inherits_class ( ' image ' , d ) :
logger . error ( " The %s recipe is an image, and therefore is not supported by this tool " % pn )
return False
if bb . data . inherits_class ( ' populate_sdk ' , d ) :
logger . error ( " The %s recipe is an SDK, and therefore is not supported by this tool " % pn )
return False
if bb . data . inherits_class ( ' packagegroup ' , d ) :
logger . error ( " The %s recipe is a packagegroup, and therefore is not supported by this tool " % pn )
return False
if bb . data . inherits_class ( ' meta ' , d ) :
logger . error ( " The %s recipe is a meta-recipe, and therefore is not supported by this tool " % pn )
return False
if bb . data . inherits_class ( ' externalsrc ' , d ) and d . getVar ( ' EXTERNALSRC ' , True ) :
logger . error ( " externalsrc is currently enabled for the %s recipe. This prevents the normal do_patch task from working. You will need to disable this first. " % pn )
return False
return True
2014-12-19 11:41:55 +00:00
def _get_recipe_file ( cooker , pn ) :
2015-05-11 13:17:01 +00:00
""" Find recipe file corresponding a package name """
2014-12-19 11:41:55 +00:00
import oe . recipeutils
recipefile = oe . recipeutils . pn_to_recipe ( cooker , pn )
if not recipefile :
skipreasons = oe . recipeutils . get_unavailable_reasons ( cooker , pn )
if skipreasons :
logger . error ( ' \n ' . join ( skipreasons ) )
else :
logger . error ( " Unable to find any recipe file matching %s " % pn )
return recipefile
2015-04-27 09:53:16 +00:00
def _parse_recipe ( config , tinfoil , pn , appends ) :
""" Parse recipe of a package """
import oe . recipeutils
recipefile = _get_recipe_file ( tinfoil . cooker , pn )
if not recipefile :
# Error already logged
return None
if appends :
append_files = tinfoil . cooker . collection . get_file_appends ( recipefile )
# Filter out appends from the workspace
append_files = [ path for path in append_files if
not path . startswith ( config . workspace_path ) ]
return oe . recipeutils . parse_recipe ( recipefile , append_files ,
tinfoil . config_data )
2014-12-19 11:41:55 +00:00
2015-04-30 09:16:07 +00:00
def _ls_tree ( directory ) :
""" Recursive listing of files in a directory """
ret = [ ]
for root , dirs , files in os . walk ( directory ) :
ret . extend ( [ os . path . relpath ( os . path . join ( root , fname ) , directory ) for
fname in files ] )
return ret
2014-12-19 11:41:55 +00:00
def extract ( args , config , basepath , workspace ) :
2015-05-11 13:17:01 +00:00
""" Entry point for the devtool ' extract ' subcommand """
2014-12-19 11:41:55 +00:00
import bb
tinfoil = setup_tinfoil ( )
2015-04-27 09:53:16 +00:00
rd = _parse_recipe ( config , tinfoil , args . recipename , True )
if not rd :
2014-12-19 11:41:55 +00:00
return - 1
srctree = os . path . abspath ( args . srctree )
initial_rev = _extract_source ( srctree , args . keep_temp , args . branch , rd )
if initial_rev :
return 0
else :
return - 1
def _extract_source ( srctree , keep_temp , devbranch , d ) :
2015-05-11 13:17:01 +00:00
""" Extract sources of a recipe """
2014-12-19 11:41:55 +00:00
import bb . event
2015-04-30 09:16:07 +00:00
import oe . recipeutils
2014-12-19 11:41:55 +00:00
def eventfilter ( name , handler , event , d ) :
2015-05-11 13:17:01 +00:00
""" Bitbake event filter for devtool extract operation """
2014-12-19 11:41:55 +00:00
if name == ' base_eventhandler ' :
return True
else :
return False
if hasattr ( bb . event , ' set_eventfilter ' ) :
bb . event . set_eventfilter ( eventfilter )
pn = d . getVar ( ' PN ' , True )
2015-03-05 10:51:20 +00:00
if not _check_compatible_recipe ( pn , d ) :
2014-12-19 11:41:55 +00:00
return None
if os . path . exists ( srctree ) :
if not os . path . isdir ( srctree ) :
logger . error ( " output path %s exists and is not a directory " % srctree )
return None
elif os . listdir ( srctree ) :
logger . error ( " output path %s already exists and is non-empty " % srctree )
return None
# Prepare for shutil.move later on
bb . utils . mkdirhier ( srctree )
os . rmdir ( srctree )
initial_rev = None
tempdir = tempfile . mkdtemp ( prefix = ' devtool ' )
try :
crd = d . createCopy ( )
# Make a subdir so we guard against WORKDIR==S
workdir = os . path . join ( tempdir , ' workdir ' )
crd . setVar ( ' WORKDIR ' , workdir )
crd . setVar ( ' T ' , os . path . join ( tempdir , ' temp ' ) )
2015-02-19 16:40:03 +00:00
if not crd . getVar ( ' S ' , True ) . startswith ( workdir ) :
# Usually a shared workdir recipe (kernel, gcc)
# Try to set a reasonable default
if bb . data . inherits_class ( ' kernel ' , d ) :
crd . setVar ( ' S ' , ' $ {WORKDIR} /source ' )
else :
crd . setVar ( ' S ' , ' $ {WORKDIR} /$ {BP} ' )
if bb . data . inherits_class ( ' kernel ' , d ) :
# We don't want to move the source to STAGING_KERNEL_DIR here
crd . setVar ( ' STAGING_KERNEL_DIR ' , ' $ {S} ' )
2014-12-19 11:41:55 +00:00
# FIXME: This is very awkward. Unfortunately it's not currently easy to properly
# execute tasks outside of bitbake itself, until then this has to suffice if we
# are to handle e.g. linux-yocto's extra tasks
executed = [ ]
def exec_task_func ( func , report ) :
2015-05-11 13:17:01 +00:00
""" Run specific bitbake task for a recipe """
2014-12-19 11:41:55 +00:00
if not func in executed :
deps = crd . getVarFlag ( func , ' deps ' )
if deps :
for taskdepfunc in deps :
exec_task_func ( taskdepfunc , True )
if report :
logger . info ( ' Executing %s ... ' % func )
fn = d . getVar ( ' FILE ' , True )
localdata = bb . build . _task_data ( fn , func , crd )
bb . build . exec_func ( func , localdata )
executed . append ( func )
logger . info ( ' Fetching %s ... ' % pn )
exec_task_func ( ' do_fetch ' , False )
logger . info ( ' Unpacking... ' )
exec_task_func ( ' do_unpack ' , False )
srcsubdir = crd . getVar ( ' S ' , True )
2015-04-30 09:16:07 +00:00
if srcsubdir == workdir :
# Find non-patch sources that were "unpacked" to srctree directory
recipe_patches = [ os . path . basename ( patch ) for patch in
oe . recipeutils . get_recipe_patches ( crd ) ]
src_files = [ fname for fname in _ls_tree ( workdir ) if
os . path . basename ( fname ) not in recipe_patches ]
# Force separate S so that patch files can be left out from srctree
srcsubdir = tempfile . mkdtemp ( dir = workdir )
crd . setVar ( ' S ' , srcsubdir )
# Move source files to S
for path in src_files :
tgt_dir = os . path . join ( srcsubdir , os . path . dirname ( path ) )
bb . utils . mkdirhier ( tgt_dir )
shutil . move ( os . path . join ( workdir , path ) , tgt_dir )
elif os . path . dirname ( srcsubdir ) != workdir :
2014-12-19 11:41:55 +00:00
# Handle if S is set to a subdirectory of the source
srcsubdir = os . path . join ( workdir , os . path . relpath ( srcsubdir , workdir ) . split ( os . sep ) [ 0 ] )
2015-04-28 11:25:30 +00:00
scriptutils . git_convert_standalone_clone ( srcsubdir )
2015-02-19 16:40:04 +00:00
2014-12-19 11:41:55 +00:00
patchdir = os . path . join ( srcsubdir , ' patches ' )
haspatches = False
if os . path . exists ( patchdir ) :
if os . listdir ( patchdir ) :
haspatches = True
else :
os . rmdir ( patchdir )
2015-02-19 16:40:03 +00:00
if bb . data . inherits_class ( ' kernel-yocto ' , d ) :
( stdout , _ ) = bb . process . run ( ' git --git-dir= " %s " rev-parse HEAD ' % crd . expand ( ' $ {WORKDIR} /git ' ) , cwd = srcsubdir )
initial_rev = stdout . rstrip ( )
else :
2014-12-19 11:41:55 +00:00
if not os . listdir ( srcsubdir ) :
logger . error ( " no source unpacked to S, perhaps the %s recipe doesn ' t use any source? " % pn )
return None
if not os . path . exists ( os . path . join ( srcsubdir , ' .git ' ) ) :
bb . process . run ( ' git init ' , cwd = srcsubdir )
bb . process . run ( ' git add . ' , cwd = srcsubdir )
bb . process . run ( ' git commit -q -m " Initial commit from upstream at version %s " ' % crd . getVar ( ' PV ' , True ) , cwd = srcsubdir )
( stdout , _ ) = bb . process . run ( ' git rev-parse HEAD ' , cwd = srcsubdir )
initial_rev = stdout . rstrip ( )
bb . process . run ( ' git checkout -b %s ' % devbranch , cwd = srcsubdir )
bb . process . run ( ' git tag -f devtool-base ' , cwd = srcsubdir )
crd . setVar ( ' PATCHTOOL ' , ' git ' )
logger . info ( ' Patching... ' )
exec_task_func ( ' do_patch ' , False )
bb . process . run ( ' git tag -f devtool-patched ' , cwd = srcsubdir )
if os . path . exists ( patchdir ) :
shutil . rmtree ( patchdir )
if haspatches :
bb . process . run ( ' git checkout patches ' , cwd = srcsubdir )
shutil . move ( srcsubdir , srctree )
logger . info ( ' Source tree extracted to %s ' % srctree )
finally :
if keep_temp :
logger . info ( ' Preserving temporary directory %s ' % tempdir )
else :
shutil . rmtree ( tempdir )
return initial_rev
def _add_md5 ( config , recipename , filename ) :
2015-05-11 13:17:01 +00:00
""" Record checksum of a recipe to the md5-file of the workspace """
2014-12-19 11:41:55 +00:00
import bb . utils
md5 = bb . utils . md5_file ( filename )
with open ( os . path . join ( config . workspace_path , ' .devtool_md5 ' ) , ' a ' ) as f :
f . write ( ' %s | %s | %s \n ' % ( recipename , os . path . relpath ( filename , config . workspace_path ) , md5 ) )
def _check_preserve ( config , recipename ) :
2015-05-11 13:17:01 +00:00
""" Check if a recipe was manually changed and needs to be saved in ' attic '
directory """
2014-12-19 11:41:55 +00:00
import bb . utils
origfile = os . path . join ( config . workspace_path , ' .devtool_md5 ' )
newfile = os . path . join ( config . workspace_path , ' .devtool_md5_new ' )
preservepath = os . path . join ( config . workspace_path , ' attic ' )
with open ( origfile , ' r ' ) as f :
with open ( newfile , ' w ' ) as tf :
for line in f . readlines ( ) :
splitline = line . rstrip ( ) . split ( ' | ' )
if splitline [ 0 ] == recipename :
removefile = os . path . join ( config . workspace_path , splitline [ 1 ] )
2015-04-27 09:53:19 +00:00
try :
md5 = bb . utils . md5_file ( removefile )
except IOError as err :
if err . errno == 2 :
# File no longer exists, skip it
continue
else :
raise
2014-12-19 11:41:55 +00:00
if splitline [ 2 ] != md5 :
bb . utils . mkdirhier ( preservepath )
preservefile = os . path . basename ( removefile )
logger . warn ( ' File %s modified since it was written, preserving in %s ' % ( preservefile , preservepath ) )
shutil . move ( removefile , os . path . join ( preservepath , preservefile ) )
else :
os . remove ( removefile )
else :
tf . write ( line )
os . rename ( newfile , origfile )
return False
def modify ( args , config , basepath , workspace ) :
2015-05-11 13:17:01 +00:00
""" Entry point for the devtool ' modify ' subcommand """
2014-12-19 11:41:55 +00:00
import bb
import oe . recipeutils
if args . recipename in workspace :
logger . error ( " recipe %s is already in your workspace " % args . recipename )
return - 1
if not args . extract :
if not os . path . isdir ( args . srctree ) :
logger . error ( " directory %s does not exist or not a directory (specify -x to extract source from recipe) " % args . srctree )
return - 1
tinfoil = setup_tinfoil ( )
2015-04-27 09:53:16 +00:00
rd = _parse_recipe ( config , tinfoil , args . recipename , True )
if not rd :
2014-12-19 11:41:55 +00:00
return - 1
2015-04-27 09:53:16 +00:00
recipefile = rd . getVar ( ' FILE ' , True )
2014-12-19 11:41:55 +00:00
2015-03-05 10:51:20 +00:00
if not _check_compatible_recipe ( args . recipename , rd ) :
return - 1
2015-02-19 16:40:01 +00:00
2014-12-19 11:41:55 +00:00
initial_rev = None
commits = [ ]
srctree = os . path . abspath ( args . srctree )
if args . extract :
initial_rev = _extract_source ( args . srctree , False , args . branch , rd )
if not initial_rev :
return - 1
# Get list of commits since this revision
( stdout , _ ) = bb . process . run ( ' git rev-list --reverse %s ..HEAD ' % initial_rev , cwd = args . srctree )
commits = stdout . split ( )
else :
if os . path . exists ( os . path . join ( args . srctree , ' .git ' ) ) :
2015-03-04 13:20:37 +00:00
# Check if it's a tree previously extracted by us
try :
( stdout , _ ) = bb . process . run ( ' git branch --contains devtool-base ' , cwd = args . srctree )
except bb . process . ExecutionError :
stdout = ' '
for line in stdout . splitlines ( ) :
if line . startswith ( ' * ' ) :
( stdout , _ ) = bb . process . run ( ' git rev-parse devtool-base ' , cwd = args . srctree )
initial_rev = stdout . rstrip ( )
if not initial_rev :
# Otherwise, just grab the head revision
( stdout , _ ) = bb . process . run ( ' git rev-parse HEAD ' , cwd = args . srctree )
initial_rev = stdout . rstrip ( )
2014-12-19 11:41:55 +00:00
2015-02-19 16:40:02 +00:00
# Check that recipe isn't using a shared workdir
2014-12-19 11:41:55 +00:00
s = rd . getVar ( ' S ' , True )
workdir = rd . getVar ( ' WORKDIR ' , True )
2015-02-19 16:40:02 +00:00
if s . startswith ( workdir ) :
# Handle if S is set to a subdirectory of the source
if s != workdir and os . path . dirname ( s ) != workdir :
srcsubdir = os . sep . join ( os . path . relpath ( s , workdir ) . split ( os . sep ) [ 1 : ] )
srctree = os . path . join ( srctree , srcsubdir )
2014-12-19 11:41:55 +00:00
appendpath = os . path . join ( config . workspace_path , ' appends ' )
if not os . path . exists ( appendpath ) :
os . makedirs ( appendpath )
appendname = os . path . splitext ( os . path . basename ( recipefile ) ) [ 0 ]
if args . wildcard :
appendname = re . sub ( r ' _.* ' , ' _ % ' , appendname )
appendfile = os . path . join ( appendpath , appendname + ' .bbappend ' )
with open ( appendfile , ' w ' ) as f :
f . write ( ' FILESEXTRAPATHS_prepend := " $ {THISDIR} /$ {PN} : " \n \n ' )
f . write ( ' inherit externalsrc \n ' )
f . write ( ' # NOTE: We use pn- overrides here to avoid affecting multiple variants in the case where the recipe uses BBCLASSEXTEND \n ' )
f . write ( ' EXTERNALSRC_pn- %s = " %s " \n ' % ( args . recipename , srctree ) )
2015-04-27 09:53:13 +00:00
b_is_s = True
2015-04-27 09:53:14 +00:00
if args . no_same_dir :
logger . info ( ' using separate build directory since --no-same-dir specified ' )
b_is_s = False
elif args . same_dir :
2015-04-27 09:53:13 +00:00
logger . info ( ' using source tree as build directory since --same-dir specified ' )
elif bb . data . inherits_class ( ' autotools-brokensep ' , rd ) :
logger . info ( ' using source tree as build directory since original recipe inherits autotools-brokensep ' )
elif rd . getVar ( ' B ' , True ) == s :
logger . info ( ' using source tree as build directory since that is the default for this recipe ' )
else :
b_is_s = False
if b_is_s :
2014-12-19 11:41:55 +00:00
f . write ( ' EXTERNALSRC_BUILD_pn- %s = " %s " \n ' % ( args . recipename , srctree ) )
2015-04-27 09:53:13 +00:00
2014-12-19 11:41:55 +00:00
if initial_rev :
f . write ( ' \n # initial_rev: %s \n ' % initial_rev )
for commit in commits :
f . write ( ' # commit: %s \n ' % commit )
_add_md5 ( config , args . recipename , appendfile )
logger . info ( ' Recipe %s now set up to build from %s ' % ( args . recipename , srctree ) )
return 0
def update_recipe ( args , config , basepath , workspace ) :
2015-05-11 13:17:01 +00:00
""" Entry point for the devtool ' update-recipe ' subcommand """
2014-12-19 11:41:55 +00:00
if not args . recipename in workspace :
logger . error ( " no recipe named %s in your workspace " % args . recipename )
return - 1
tinfoil = setup_tinfoil ( )
import bb
from oe . patch import GitApplyTree
import oe . recipeutils
2015-04-27 09:53:16 +00:00
rd = _parse_recipe ( config , tinfoil , args . recipename , True )
if not rd :
2014-12-19 11:41:55 +00:00
return - 1
2015-04-27 09:53:16 +00:00
recipefile = rd . getVar ( ' FILE ' , True )
2014-12-19 11:41:55 +00:00
2015-04-27 09:53:20 +00:00
# Get initial revision from bbappend
append = os . path . join ( config . workspace_path , ' appends ' , ' %s .bbappend ' % os . path . splitext ( os . path . basename ( recipefile ) ) [ 0 ] )
if not os . path . exists ( append ) :
logger . error ( ' unable to find workspace bbappend for recipe %s ' % args . recipename )
return - 1
2015-02-19 16:40:00 +00:00
orig_src_uri = rd . getVar ( ' SRC_URI ' , False ) or ' '
if args . mode == ' auto ' :
if ' git:// ' in orig_src_uri :
mode = ' srcrev '
else :
mode = ' patch '
else :
mode = args . mode
def remove_patches ( srcuri , patchlist ) :
2015-05-11 13:17:01 +00:00
""" Remove patches """
2015-02-19 16:40:00 +00:00
updated = False
for patch in patchlist :
patchfile = os . path . basename ( patch )
for i in xrange ( len ( srcuri ) ) :
if srcuri [ i ] . startswith ( ' file:// ' ) and os . path . basename ( srcuri [ i ] ) . split ( ' ; ' ) [ 0 ] == patchfile :
logger . info ( ' Removing patch %s ' % patchfile )
srcuri . pop ( i )
# FIXME "git rm" here would be nice if the file in question is tracked
# FIXME there's a chance that this file is referred to by another recipe, in which case deleting wouldn't be the right thing to do
if patch . startswith ( os . path . dirname ( recipefile ) ) :
os . remove ( patch )
updated = True
break
return updated
srctree = workspace [ args . recipename ]
2015-04-27 09:53:21 +00:00
# Get HEAD revision
try :
2015-02-19 16:40:00 +00:00
( stdout , _ ) = bb . process . run ( ' git rev-parse HEAD ' , cwd = srctree )
2015-04-27 09:53:21 +00:00
except bb . process . ExecutionError as err :
print ( ' Failed to get HEAD revision in %s : %s ' % ( srctree , err ) )
return 1
srcrev = stdout . strip ( )
if len ( srcrev ) != 40 :
logger . error ( ' Invalid hash returned by git: %s ' % stdout )
return 1
2015-02-19 16:40:00 +00:00
2015-04-27 09:53:21 +00:00
if mode == ' srcrev ' :
2015-02-19 16:40:00 +00:00
logger . info ( ' Updating SRCREV in recipe %s ' % os . path . basename ( recipefile ) )
patchfields = { }
patchfields [ ' SRCREV ' ] = srcrev
if not args . no_remove :
# Find list of existing patches in recipe file
existing_patches = oe . recipeutils . get_recipe_patches ( rd )
old_srcrev = ( rd . getVar ( ' SRCREV ' , False ) or ' ' )
tempdir = tempfile . mkdtemp ( prefix = ' devtool ' )
removepatches = [ ]
try :
GitApplyTree . extractPatches ( srctree , old_srcrev , tempdir )
newpatches = os . listdir ( tempdir )
for patch in existing_patches :
patchfile = os . path . basename ( patch )
if patchfile in newpatches :
removepatches . append ( patch )
finally :
shutil . rmtree ( tempdir )
if removepatches :
srcuri = ( rd . getVar ( ' SRC_URI ' , False ) or ' ' ) . split ( )
if remove_patches ( srcuri , removepatches ) :
patchfields [ ' SRC_URI ' ] = ' ' . join ( srcuri )
2015-04-16 16:37:57 +00:00
oe . recipeutils . patch_recipe ( tinfoil . config_data , recipefile , patchfields )
2015-02-19 16:40:00 +00:00
if not ' git:// ' in orig_src_uri :
logger . info ( ' You will need to update SRC_URI within the recipe to point to a git repository where you have pushed your changes ' )
elif mode == ' patch ' :
commits = [ ]
update_rev = None
if args . initial_rev :
initial_rev = args . initial_rev
else :
initial_rev = None
2015-04-27 09:53:20 +00:00
with open ( append , ' r ' ) as f :
2015-02-19 16:40:00 +00:00
for line in f :
if line . startswith ( ' # initial_rev: ' ) :
initial_rev = line . split ( ' : ' ) [ - 1 ] . strip ( )
elif line . startswith ( ' # commit: ' ) :
commits . append ( line . split ( ' : ' ) [ - 1 ] . strip ( ) )
if initial_rev :
# Find first actually changed revision
( stdout , _ ) = bb . process . run ( ' git rev-list --reverse %s ..HEAD ' % initial_rev , cwd = srctree )
newcommits = stdout . split ( )
for i in xrange ( min ( len ( commits ) , len ( newcommits ) ) ) :
if newcommits [ i ] == commits [ i ] :
update_rev = commits [ i ]
if not initial_rev :
logger . error ( ' Unable to find initial revision - please specify it with --initial-rev ' )
return - 1
if not update_rev :
update_rev = initial_rev
# Find list of existing patches in recipe file
existing_patches = oe . recipeutils . get_recipe_patches ( rd )
removepatches = [ ]
if not args . no_remove :
# Get all patches from source tree and check if any should be removed
tempdir = tempfile . mkdtemp ( prefix = ' devtool ' )
try :
GitApplyTree . extractPatches ( srctree , initial_rev , tempdir )
newpatches = os . listdir ( tempdir )
for patch in existing_patches :
patchfile = os . path . basename ( patch )
if patchfile not in newpatches :
removepatches . append ( patch )
finally :
shutil . rmtree ( tempdir )
# Get updated patches from source tree
2014-12-19 11:41:55 +00:00
tempdir = tempfile . mkdtemp ( prefix = ' devtool ' )
try :
2015-02-19 16:40:00 +00:00
GitApplyTree . extractPatches ( srctree , update_rev , tempdir )
# Match up and replace existing patches with corresponding new patches
updatepatches = False
updaterecipe = False
2014-12-19 11:41:55 +00:00
newpatches = os . listdir ( tempdir )
for patch in existing_patches :
patchfile = os . path . basename ( patch )
2015-02-19 16:40:00 +00:00
if patchfile in newpatches :
logger . info ( ' Updating patch %s ' % patchfile )
shutil . move ( os . path . join ( tempdir , patchfile ) , patch )
newpatches . remove ( patchfile )
updatepatches = True
srcuri = ( rd . getVar ( ' SRC_URI ' , False ) or ' ' ) . split ( )
if newpatches :
# Add any patches left over
patchdir = os . path . join ( os . path . dirname ( recipefile ) , rd . getVar ( ' BPN ' , True ) )
bb . utils . mkdirhier ( patchdir )
for patchfile in newpatches :
logger . info ( ' Adding new patch %s ' % patchfile )
shutil . move ( os . path . join ( tempdir , patchfile ) , os . path . join ( patchdir , patchfile ) )
srcuri . append ( ' file:// %s ' % patchfile )
updaterecipe = True
if removepatches :
if remove_patches ( srcuri , removepatches ) :
updaterecipe = True
if updaterecipe :
logger . info ( ' Updating recipe %s ' % os . path . basename ( recipefile ) )
2015-04-16 16:37:57 +00:00
oe . recipeutils . patch_recipe ( tinfoil . config_data ,
recipefile , { ' SRC_URI ' : ' ' . join ( srcuri ) } )
2015-02-19 16:40:00 +00:00
elif not updatepatches :
# Neither patches nor recipe were updated
logger . info ( ' No patches need updating ' )
2014-12-19 11:41:55 +00:00
finally :
shutil . rmtree ( tempdir )
2015-02-19 16:40:00 +00:00
else :
logger . error ( ' update_recipe: invalid mode %s ' % mode )
return 1
2014-12-19 11:41:55 +00:00
return 0
def status ( args , config , basepath , workspace ) :
2015-05-11 13:17:01 +00:00
""" Entry point for the devtool ' status ' subcommand """
2014-12-19 11:41:55 +00:00
if workspace :
for recipe , value in workspace . iteritems ( ) :
print ( " %s : %s " % ( recipe , value ) )
else :
logger . info ( ' No recipes currently in your workspace - you can use " devtool modify " to work on an existing recipe or " devtool add " to add a new one ' )
return 0
def reset ( args , config , basepath , workspace ) :
2015-05-11 13:17:01 +00:00
""" Entry point for the devtool ' reset ' subcommand """
2015-05-14 09:18:18 +00:00
import bb
2015-03-04 12:56:13 +00:00
if args . recipename :
if args . all :
logger . error ( " Recipe cannot be specified if -a/--all is used " )
return - 1
elif not args . recipename in workspace :
logger . error ( " no recipe named %s in your workspace " % args . recipename )
return - 1
elif not args . all :
logger . error ( " Recipe must be specified, or specify -a/--all to reset all recipes " )
2014-12-19 11:41:55 +00:00
return - 1
2015-02-19 16:39:57 +00:00
2015-03-04 12:56:13 +00:00
if args . all :
recipes = workspace
else :
recipes = [ args . recipename ]
for pn in recipes :
if not args . no_clean :
logger . info ( ' Cleaning sysroot for recipe %s ... ' % pn )
2015-05-14 09:18:18 +00:00
try :
exec_build_env_command ( config . init_path , basepath , ' bitbake -c clean %s ' % pn )
except bb . process . ExecutionError as e :
logger . error ( ' Command \' %s \' failed, output: \n %s \n If you wish, you may specify -n/--no-clean to skip running this command when resetting ' % ( e . command , e . stdout ) )
return 1
2015-02-19 16:39:57 +00:00
2015-03-04 12:56:13 +00:00
_check_preserve ( config , pn )
2014-12-19 11:41:55 +00:00
2015-03-04 12:56:13 +00:00
preservepath = os . path . join ( config . workspace_path , ' attic ' , pn )
def preservedir ( origdir ) :
if os . path . exists ( origdir ) :
for fn in os . listdir ( origdir ) :
logger . warn ( ' Preserving %s in %s ' % ( fn , preservepath ) )
bb . utils . mkdirhier ( preservepath )
shutil . move ( os . path . join ( origdir , fn ) , os . path . join ( preservepath , fn ) )
os . rmdir ( origdir )
preservedir ( os . path . join ( config . workspace_path , ' recipes ' , pn ) )
# We don't automatically create this dir next to appends, but the user can
preservedir ( os . path . join ( config . workspace_path , ' appends ' , pn ) )
2014-12-19 11:41:55 +00:00
return 0
def build ( args , config , basepath , workspace ) :
2015-05-11 13:17:01 +00:00
""" Entry point for the devtool ' build ' subcommand """
2014-12-19 11:41:55 +00:00
import bb
if not args . recipename in workspace :
logger . error ( " no recipe named %s in your workspace " % args . recipename )
return - 1
2015-02-19 16:39:58 +00:00
build_task = config . get ( ' Build ' , ' build_task ' , ' populate_sysroot ' )
2015-05-14 09:18:18 +00:00
try :
exec_build_env_command ( config . init_path , basepath , ' bitbake -c %s %s ' % ( build_task , args . recipename ) , watch = True )
except bb . process . ExecutionError as e :
# We've already seen the output since watch=True, so just ensure we return something to the user
return e . exitcode
2014-12-19 11:41:55 +00:00
return 0
def register_commands ( subparsers , context ) :
2015-05-11 13:17:01 +00:00
""" Register devtool subcommands from this plugin """
2014-12-19 11:41:55 +00:00
parser_add = subparsers . add_parser ( ' add ' , help = ' Add a new recipe ' ,
2015-04-28 10:21:55 +00:00
description = ' Adds a new recipe ' )
2014-12-19 11:41:55 +00:00
parser_add . add_argument ( ' recipename ' , help = ' Name for new recipe to add ' )
parser_add . add_argument ( ' srctree ' , help = ' Path to external source tree ' )
2015-02-19 16:39:56 +00:00
parser_add . add_argument ( ' --same-dir ' , ' -s ' , help = ' Build in same directory as source ' , action = " store_true " )
2015-04-28 10:21:55 +00:00
parser_add . add_argument ( ' --fetch ' , ' -f ' , help = ' Fetch the specified URI and extract it to create the source tree ' , metavar = ' URI ' )
2014-12-19 11:41:55 +00:00
parser_add . add_argument ( ' --version ' , ' -V ' , help = ' Version to use within recipe (PV) ' )
parser_add . set_defaults ( func = add )
2015-03-04 12:46:29 +00:00
parser_modify = subparsers . add_parser ( ' modify ' , help = ' Modify the source for an existing recipe ' ,
2015-02-05 14:03:59 +00:00
description = ' Enables modifying the source for an existing recipe ' ,
2014-12-19 11:41:55 +00:00
formatter_class = argparse . ArgumentDefaultsHelpFormatter )
2015-03-04 12:46:29 +00:00
parser_modify . add_argument ( ' recipename ' , help = ' Name for recipe to edit ' )
parser_modify . add_argument ( ' srctree ' , help = ' Path to external source tree ' )
parser_modify . add_argument ( ' --wildcard ' , ' -w ' , action = " store_true " , help = ' Use wildcard for unversioned bbappend ' )
parser_modify . add_argument ( ' --extract ' , ' -x ' , action = " store_true " , help = ' Extract source as well ' )
2015-04-27 09:53:14 +00:00
group = parser_modify . add_mutually_exclusive_group ( )
group . add_argument ( ' --same-dir ' , ' -s ' , help = ' Build in same directory as source ' , action = " store_true " )
group . add_argument ( ' --no-same-dir ' , help = ' Force build in a separate build directory ' , action = " store_true " )
2015-03-04 12:46:29 +00:00
parser_modify . add_argument ( ' --branch ' , ' -b ' , default = " devtool " , help = ' Name for development branch to checkout (only when using -x) ' )
parser_modify . set_defaults ( func = modify )
parser_extract = subparsers . add_parser ( ' extract ' , help = ' Extract the source for an existing recipe ' ,
2015-02-05 14:03:59 +00:00
description = ' Extracts the source for an existing recipe ' ,
2014-12-19 11:41:55 +00:00
formatter_class = argparse . ArgumentDefaultsHelpFormatter )
2015-03-04 12:46:29 +00:00
parser_extract . add_argument ( ' recipename ' , help = ' Name for recipe to extract the source for ' )
parser_extract . add_argument ( ' srctree ' , help = ' Path to where to extract the source tree ' )
parser_extract . add_argument ( ' --branch ' , ' -b ' , default = " devtool " , help = ' Name for development branch to checkout ' )
parser_extract . add_argument ( ' --keep-temp ' , action = " store_true " , help = ' Keep temporary directory (for debugging) ' )
parser_extract . set_defaults ( func = extract )
2014-12-19 11:41:55 +00:00
2015-03-04 12:46:29 +00:00
parser_update_recipe = subparsers . add_parser ( ' update-recipe ' , help = ' Apply changes from external source tree to recipe ' ,
2015-02-19 16:40:00 +00:00
description = ' Applies changes from external source tree to a recipe (updating/adding/removing patches as necessary, or by updating SRCREV) ' )
2015-03-04 12:46:29 +00:00
parser_update_recipe . add_argument ( ' recipename ' , help = ' Name of recipe to update ' )
parser_update_recipe . add_argument ( ' --mode ' , ' -m ' , choices = [ ' patch ' , ' srcrev ' , ' auto ' ] , default = ' auto ' , help = ' Update mode (where %(metavar)s is %(choices)s ; default is %(default)s ) ' , metavar = ' MODE ' )
parser_update_recipe . add_argument ( ' --initial-rev ' , help = ' Starting revision for patches ' )
parser_update_recipe . add_argument ( ' --no-remove ' , ' -n ' , action = " store_true " , help = ' Don \' t remove patches, only add or update ' )
parser_update_recipe . set_defaults ( func = update_recipe )
2014-12-19 11:41:55 +00:00
2015-02-05 14:03:59 +00:00
parser_status = subparsers . add_parser ( ' status ' , help = ' Show workspace status ' ,
description = ' Lists recipes currently in your workspace and the paths to their respective external source trees ' ,
2014-12-19 11:41:55 +00:00
formatter_class = argparse . ArgumentDefaultsHelpFormatter )
parser_status . set_defaults ( func = status )
2015-02-05 14:03:59 +00:00
parser_build = subparsers . add_parser ( ' build ' , help = ' Build a recipe ' ,
2015-02-19 16:39:58 +00:00
description = ' Builds the specified recipe using bitbake ' ,
2014-12-19 11:41:55 +00:00
formatter_class = argparse . ArgumentDefaultsHelpFormatter )
parser_build . add_argument ( ' recipename ' , help = ' Recipe to build ' )
parser_build . set_defaults ( func = build )
parser_reset = subparsers . add_parser ( ' reset ' , help = ' Remove a recipe from your workspace ' ,
2015-02-05 14:03:59 +00:00
description = ' Removes the specified recipe from your workspace (resetting its state) ' ,
2014-12-19 11:41:55 +00:00
formatter_class = argparse . ArgumentDefaultsHelpFormatter )
2015-03-04 12:56:13 +00:00
parser_reset . add_argument ( ' recipename ' , nargs = ' ? ' , help = ' Recipe to reset ' )
parser_reset . add_argument ( ' --all ' , ' -a ' , action = " store_true " , help = ' Reset all recipes (clear workspace) ' )
2015-02-19 16:39:57 +00:00
parser_reset . add_argument ( ' --no-clean ' , ' -n ' , action = " store_true " , help = ' Don \' t clean the sysroot to remove recipe output ' )
2014-12-19 11:41:55 +00:00
parser_reset . set_defaults ( func = reset )