summaryrefslogtreecommitdiffstats
path: root/scripts/lib/devtool
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/devtool')
-rw-r--r--scripts/lib/devtool/__init__.py4
-rw-r--r--scripts/lib/devtool/build.py2
-rw-r--r--scripts/lib/devtool/build_sdk.py7
-rw-r--r--scripts/lib/devtool/ide_plugins/ide_code.py1
-rwxr-xr-xscripts/lib/devtool/ide_sdk.py109
-rw-r--r--scripts/lib/devtool/menuconfig.py7
-rw-r--r--scripts/lib/devtool/standard.py320
-rw-r--r--scripts/lib/devtool/upgrade.py68
-rw-r--r--scripts/lib/devtool/utilcmds.py2
9 files changed, 206 insertions, 314 deletions
diff --git a/scripts/lib/devtool/__init__.py b/scripts/lib/devtool/__init__.py
index 6133c1c5b4..fa6e1a34fd 100644
--- a/scripts/lib/devtool/__init__.py
+++ b/scripts/lib/devtool/__init__.py
@@ -234,7 +234,7 @@ def setup_git_repo(repodir, version, devbranch, basetag='devtool-base', d=None):
234 f.write(line) 234 f.write(line)
235 235
236 bb.process.run('git checkout -b %s' % devbranch, cwd=repodir) 236 bb.process.run('git checkout -b %s' % devbranch, cwd=repodir)
237 bb.process.run('git tag -f %s' % basetag, cwd=repodir) 237 bb.process.run('git tag -f --no-sign %s' % basetag, cwd=repodir)
238 238
239 # if recipe unpacks another git repo inside S, we need to declare it as a regular git submodule now, 239 # if recipe unpacks another git repo inside S, we need to declare it as a regular git submodule now,
240 # so we will be able to tag branches on it and extract patches when doing finish/update on the recipe 240 # so we will be able to tag branches on it and extract patches when doing finish/update on the recipe
@@ -256,7 +256,7 @@ def setup_git_repo(repodir, version, devbranch, basetag='devtool-base', d=None):
256 oe.patch.GitApplyTree.commitIgnored("Add additional submodule from SRC_URI", dir=os.path.join(root, ".."), d=d) 256 oe.patch.GitApplyTree.commitIgnored("Add additional submodule from SRC_URI", dir=os.path.join(root, ".."), d=d)
257 found = False 257 found = False
258 if os.path.exists(os.path.join(repodir, '.gitmodules')): 258 if os.path.exists(os.path.join(repodir, '.gitmodules')):
259 bb.process.run('git submodule foreach --recursive "git tag -f %s"' % basetag, cwd=repodir) 259 bb.process.run('git submodule foreach --recursive "git tag -f --no-sign %s"' % basetag, cwd=repodir)
260 260
261def recipe_to_append(recipefile, config, wildcard=False): 261def recipe_to_append(recipefile, config, wildcard=False):
262 """ 262 """
diff --git a/scripts/lib/devtool/build.py b/scripts/lib/devtool/build.py
index 935ffab46c..0b2c3d33dc 100644
--- a/scripts/lib/devtool/build.py
+++ b/scripts/lib/devtool/build.py
@@ -49,7 +49,7 @@ def build(args, config, basepath, workspace):
49 rd = parse_recipe(config, tinfoil, args.recipename, appends=True, filter_workspace=False) 49 rd = parse_recipe(config, tinfoil, args.recipename, appends=True, filter_workspace=False)
50 if not rd: 50 if not rd:
51 return 1 51 return 1
52 deploytask = 'do_deploy' in rd.getVar('__BBTASKS') 52 deploytask = 'do_deploy' in bb.build.listtasks(rd)
53 finally: 53 finally:
54 tinfoil.shutdown() 54 tinfoil.shutdown()
55 55
diff --git a/scripts/lib/devtool/build_sdk.py b/scripts/lib/devtool/build_sdk.py
index 1cd4831d2b..990303982c 100644
--- a/scripts/lib/devtool/build_sdk.py
+++ b/scripts/lib/devtool/build_sdk.py
@@ -5,14 +5,7 @@
5# SPDX-License-Identifier: GPL-2.0-only 5# SPDX-License-Identifier: GPL-2.0-only
6# 6#
7 7
8import os
9import subprocess
10import logging 8import logging
11import glob
12import shutil
13import errno
14import sys
15import tempfile
16from devtool import DevtoolError 9from devtool import DevtoolError
17from devtool import build_image 10from devtool import build_image
18 11
diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py
index a62b93224e..ee5bb57265 100644
--- a/scripts/lib/devtool/ide_plugins/ide_code.py
+++ b/scripts/lib/devtool/ide_plugins/ide_code.py
@@ -161,7 +161,6 @@ class IdeVSCode(IdeBase):
161 if modified_recipe.build_tool is not BuildTool.CMAKE: 161 if modified_recipe.build_tool is not BuildTool.CMAKE:
162 return 162 return
163 recommendations += [ 163 recommendations += [
164 "twxs.cmake",
165 "ms-vscode.cmake-tools", 164 "ms-vscode.cmake-tools",
166 "ms-vscode.cpptools", 165 "ms-vscode.cpptools",
167 "ms-vscode.cpptools-extension-pack", 166 "ms-vscode.cpptools-extension-pack",
diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py
index 7807b322b3..931408fa74 100755
--- a/scripts/lib/devtool/ide_sdk.py
+++ b/scripts/lib/devtool/ide_sdk.py
@@ -167,7 +167,7 @@ class RecipeImage:
167 self.__rootfs_dbg = os.path.join(workdir, 'rootfs-dbg') 167 self.__rootfs_dbg = os.path.join(workdir, 'rootfs-dbg')
168 168
169 self.gdbserver_missing = 'gdbserver' not in image_d.getVar( 169 self.gdbserver_missing = 'gdbserver' not in image_d.getVar(
170 'IMAGE_INSTALL') 170 'IMAGE_INSTALL') and 'tools-debug' not in image_d.getVar('IMAGE_FEATURES')
171 171
172 @property 172 @property
173 def debug_support(self): 173 def debug_support(self):
@@ -288,6 +288,7 @@ class RecipeModified:
288 self.bblayers = None 288 self.bblayers = None
289 self.bpn = None 289 self.bpn = None
290 self.d = None 290 self.d = None
291 self.debug_build = None
291 self.fakerootcmd = None 292 self.fakerootcmd = None
292 self.fakerootenv = None 293 self.fakerootenv = None
293 self.libdir = None 294 self.libdir = None
@@ -333,7 +334,7 @@ class RecipeModified:
333 self.srctree = workspace[workspacepn]['srctree'] 334 self.srctree = workspace[workspacepn]['srctree']
334 # Need to grab this here in case the source is within a subdirectory 335 # Need to grab this here in case the source is within a subdirectory
335 self.real_srctree = get_real_srctree( 336 self.real_srctree = get_real_srctree(
336 self.srctree, recipe_d.getVar('S'), recipe_d.getVar('WORKDIR')) 337 self.srctree, recipe_d.getVar('S'), recipe_d.getVar('UNPACKDIR'))
337 self.bbappend = workspace[workspacepn]['bbappend'] 338 self.bbappend = workspace[workspacepn]['bbappend']
338 339
339 self.ide_sdk_dir = os.path.join( 340 self.ide_sdk_dir = os.path.join(
@@ -348,6 +349,7 @@ class RecipeModified:
348 self.bpn = recipe_d.getVar('BPN') 349 self.bpn = recipe_d.getVar('BPN')
349 self.cxx = recipe_d.getVar('CXX') 350 self.cxx = recipe_d.getVar('CXX')
350 self.d = recipe_d.getVar('D') 351 self.d = recipe_d.getVar('D')
352 self.debug_build = recipe_d.getVar('DEBUG_BUILD')
351 self.fakerootcmd = recipe_d.getVar('FAKEROOTCMD') 353 self.fakerootcmd = recipe_d.getVar('FAKEROOTCMD')
352 self.fakerootenv = recipe_d.getVar('FAKEROOTENV') 354 self.fakerootenv = recipe_d.getVar('FAKEROOTENV')
353 self.libdir = recipe_d.getVar('libdir') 355 self.libdir = recipe_d.getVar('libdir')
@@ -389,17 +391,6 @@ class RecipeModified:
389 self.recipe_id = self.bpn + "-" + self.package_arch 391 self.recipe_id = self.bpn + "-" + self.package_arch
390 self.recipe_id_pretty = self.bpn + ": " + self.package_arch 392 self.recipe_id_pretty = self.bpn + ": " + self.package_arch
391 393
392 def append_to_bbappend(self, append_text):
393 with open(self.bbappend, 'a') as bbap:
394 bbap.write(append_text)
395
396 def remove_from_bbappend(self, append_text):
397 with open(self.bbappend, 'r') as bbap:
398 text = bbap.read()
399 new_text = text.replace(append_text, '')
400 with open(self.bbappend, 'w') as bbap:
401 bbap.write(new_text)
402
403 @staticmethod 394 @staticmethod
404 def is_valid_shell_variable(var): 395 def is_valid_shell_variable(var):
405 """Skip strange shell variables like systemd 396 """Skip strange shell variables like systemd
@@ -412,34 +403,6 @@ class RecipeModified:
412 return True 403 return True
413 return False 404 return False
414 405
415 def debug_build_config(self, args):
416 """Explicitely set for example CMAKE_BUILD_TYPE to Debug if not defined otherwise"""
417 if self.build_tool is BuildTool.CMAKE:
418 append_text = os.linesep + \
419 'OECMAKE_ARGS:append = " -DCMAKE_BUILD_TYPE:STRING=Debug"' + os.linesep
420 if args.debug_build_config and not 'CMAKE_BUILD_TYPE' in self.cmake_cache_vars:
421 self.cmake_cache_vars['CMAKE_BUILD_TYPE'] = {
422 "type": "STRING",
423 "value": "Debug",
424 }
425 self.append_to_bbappend(append_text)
426 elif 'CMAKE_BUILD_TYPE' in self.cmake_cache_vars:
427 del self.cmake_cache_vars['CMAKE_BUILD_TYPE']
428 self.remove_from_bbappend(append_text)
429 elif self.build_tool is BuildTool.MESON:
430 append_text = os.linesep + 'MESON_BUILDTYPE = "debug"' + os.linesep
431 if args.debug_build_config and self.meson_buildtype != "debug":
432 self.mesonopts.replace(
433 '--buildtype ' + self.meson_buildtype, '--buildtype debug')
434 self.append_to_bbappend(append_text)
435 elif self.meson_buildtype == "debug":
436 self.mesonopts.replace(
437 '--buildtype debug', '--buildtype plain')
438 self.remove_from_bbappend(append_text)
439 elif args.debug_build_config:
440 logger.warn(
441 "--debug-build-config is not implemented for this build tool yet.")
442
443 def solib_search_path(self, image): 406 def solib_search_path(self, image):
444 """Search for debug symbols in the rootfs and rootfs-dbg 407 """Search for debug symbols in the rootfs and rootfs-dbg
445 408
@@ -493,7 +456,7 @@ class RecipeModified:
493 456
494 vars = (key for key in d.keys() if not key.startswith( 457 vars = (key for key in d.keys() if not key.startswith(
495 "__") and not d.getVarFlag(key, "func", False)) 458 "__") and not d.getVarFlag(key, "func", False))
496 for var in vars: 459 for var in sorted(vars):
497 func = d.getVarFlag(var, "func", False) 460 func = d.getVarFlag(var, "func", False)
498 if d.getVarFlag(var, 'python', False) and func: 461 if d.getVarFlag(var, 'python', False) and func:
499 continue 462 continue
@@ -545,7 +508,7 @@ class RecipeModified:
545 cache_vars = {} 508 cache_vars = {}
546 oecmake_args = d.getVar('OECMAKE_ARGS').split() 509 oecmake_args = d.getVar('OECMAKE_ARGS').split()
547 extra_oecmake = d.getVar('EXTRA_OECMAKE').split() 510 extra_oecmake = d.getVar('EXTRA_OECMAKE').split()
548 for param in oecmake_args + extra_oecmake: 511 for param in sorted(oecmake_args + extra_oecmake):
549 d_pref = "-D" 512 d_pref = "-D"
550 if param.startswith(d_pref): 513 if param.startswith(d_pref):
551 param = param[len(d_pref):] 514 param = param[len(d_pref):]
@@ -712,42 +675,6 @@ class RecipeModified:
712 binaries.append(abs_name[d_len:]) 675 binaries.append(abs_name[d_len:])
713 return sorted(binaries) 676 return sorted(binaries)
714 677
715 def gen_delete_package_dirs(self):
716 """delete folders of package tasks
717
718 This is a workaround for and issue with recipes having their sources
719 downloaded as file://
720 This likely breaks pseudo like:
721 path mismatch [3 links]: ino 79147802 db
722 .../build/tmp/.../cmake-example/1.0/package/usr/src/debug/
723 cmake-example/1.0-r0/oe-local-files/cpp-example-lib.cpp
724 .../build/workspace/sources/cmake-example/oe-local-files/cpp-example-lib.cpp
725 Since the files are anyway outdated lets deleted them (also from pseudo's db) to workaround this issue.
726 """
727 cmd_lines = ['#!/bin/sh']
728
729 # Set up the appropriate environment
730 newenv = dict(os.environ)
731 for varvalue in self.fakerootenv.split():
732 if '=' in varvalue:
733 splitval = varvalue.split('=', 1)
734 newenv[splitval[0]] = splitval[1]
735
736 # Replicate the environment variables from bitbake
737 for var, val in newenv.items():
738 if not RecipeModified.is_valid_shell_variable(var):
739 continue
740 cmd_lines.append('%s="%s"' % (var, val))
741 cmd_lines.append('export %s' % var)
742
743 # Delete the folders
744 pkg_dirs = ' '.join([os.path.join(self.workdir, d) for d in [
745 "package", "packages-split", "pkgdata", "sstate-install-package", "debugsources.list", "*.spec"]])
746 cmd = "%s rm -rf %s" % (self.fakerootcmd, pkg_dirs)
747 cmd_lines.append('%s || { "%s failed"; exit 1; }' % (cmd, cmd))
748
749 return self.write_script(cmd_lines, 'delete_package_dirs')
750
751 def gen_deploy_target_script(self, args): 678 def gen_deploy_target_script(self, args):
752 """Generate a script which does what devtool deploy-target does 679 """Generate a script which does what devtool deploy-target does
753 680
@@ -785,8 +712,6 @@ class RecipeModified:
785 """Generate a script which does install and deploy""" 712 """Generate a script which does install and deploy"""
786 cmd_lines = ['#!/bin/bash'] 713 cmd_lines = ['#!/bin/bash']
787 714
788 cmd_lines.append(self.gen_delete_package_dirs())
789
790 # . oe-init-build-env $BUILDDIR 715 # . oe-init-build-env $BUILDDIR
791 # Note: Sourcing scripts with arguments requires bash 716 # Note: Sourcing scripts with arguments requires bash
792 cmd_lines.append('cd "%s" || { echo "cd %s failed"; exit 1; }' % ( 717 cmd_lines.append('cd "%s" || { echo "cd %s failed"; exit 1; }' % (
@@ -988,6 +913,13 @@ def ide_setup(args, config, basepath, workspace):
988 recipe_modified.gen_meson_wrapper() 913 recipe_modified.gen_meson_wrapper()
989 ide.setup_modified_recipe( 914 ide.setup_modified_recipe(
990 args, recipe_image, recipe_modified) 915 args, recipe_image, recipe_modified)
916
917 if recipe_modified.debug_build != '1':
918 logger.warn(
919 'Recipe %s is compiled with release build configuration. '
920 'You might want to add DEBUG_BUILD = "1" to %s. '
921 'Note that devtool modify --debug-build can do this automatically.',
922 recipe_modified.name, recipe_modified.bbappend)
991 else: 923 else:
992 raise DevtoolError("Must not end up here.") 924 raise DevtoolError("Must not end up here.")
993 925
@@ -995,6 +927,15 @@ def ide_setup(args, config, basepath, workspace):
995def register_commands(subparsers, context): 927def register_commands(subparsers, context):
996 """Register devtool subcommands from this plugin""" 928 """Register devtool subcommands from this plugin"""
997 929
930 # The ide-sdk command bootstraps the SDK from the bitbake environment before the IDE
931 # configuration is generated. In the case of the eSDK, the bootstrapping is performed
932 # during the installation of the eSDK installer. Running the ide-sdk plugin from an
933 # eSDK installer-based setup would require skipping the bootstrapping and probably
934 # taking some other differences into account when generating the IDE configurations.
935 # This would be possible. But it is not implemented.
936 if context.fixed_setup:
937 return
938
998 global ide_plugins 939 global ide_plugins
999 940
1000 # Search for IDE plugins in all sub-folders named ide_plugins where devtool seraches for plugins. 941 # Search for IDE plugins in all sub-folders named ide_plugins where devtool seraches for plugins.
@@ -1015,7 +956,7 @@ def register_commands(subparsers, context):
1015 help='Setup the SDK and configure the IDE') 956 help='Setup the SDK and configure the IDE')
1016 parser_ide_sdk.add_argument( 957 parser_ide_sdk.add_argument(
1017 'recipenames', nargs='+', help='Generate an IDE configuration suitable to work on the given recipes.\n' 958 'recipenames', nargs='+', help='Generate an IDE configuration suitable to work on the given recipes.\n'
1018 'Depending on the --mode paramter different types of SDKs and IDE configurations are generated.') 959 'Depending on the --mode parameter different types of SDKs and IDE configurations are generated.')
1019 parser_ide_sdk.add_argument( 960 parser_ide_sdk.add_argument(
1020 '-m', '--mode', type=DevtoolIdeMode, default=DevtoolIdeMode.modified, 961 '-m', '--mode', type=DevtoolIdeMode, default=DevtoolIdeMode.modified,
1021 help='Different SDK types are supported:\n' 962 help='Different SDK types are supported:\n'
@@ -1052,7 +993,7 @@ def register_commands(subparsers, context):
1052 parser_ide_sdk.add_argument( 993 parser_ide_sdk.add_argument(
1053 '-I', '--key', help='Specify ssh private key for connection to the target') 994 '-I', '--key', help='Specify ssh private key for connection to the target')
1054 parser_ide_sdk.add_argument( 995 parser_ide_sdk.add_argument(
1055 '--skip-bitbake', help='Generate IDE configuration but skip calling bibtake to update the SDK.', action='store_true') 996 '--skip-bitbake', help='Generate IDE configuration but skip calling bitbake to update the SDK', action='store_true')
1056 parser_ide_sdk.add_argument( 997 parser_ide_sdk.add_argument(
1057 '-k', '--bitbake-k', help='Pass -k parameter to bitbake', action='store_true') 998 '-k', '--bitbake-k', help='Pass -k parameter to bitbake', action='store_true')
1058 parser_ide_sdk.add_argument( 999 parser_ide_sdk.add_argument(
@@ -1065,6 +1006,4 @@ def register_commands(subparsers, context):
1065 '-p', '--no-preserve', help='Do not preserve existing files', action='store_true') 1006 '-p', '--no-preserve', help='Do not preserve existing files', action='store_true')
1066 parser_ide_sdk.add_argument( 1007 parser_ide_sdk.add_argument(
1067 '--no-check-space', help='Do not check for available space before deploying', action='store_true') 1008 '--no-check-space', help='Do not check for available space before deploying', action='store_true')
1068 parser_ide_sdk.add_argument(
1069 '--debug-build-config', help='Use debug build flags, for example set CMAKE_BUILD_TYPE=Debug', action='store_true')
1070 parser_ide_sdk.set_defaults(func=ide_setup) 1009 parser_ide_sdk.set_defaults(func=ide_setup)
diff --git a/scripts/lib/devtool/menuconfig.py b/scripts/lib/devtool/menuconfig.py
index 18daef30c3..1054960551 100644
--- a/scripts/lib/devtool/menuconfig.py
+++ b/scripts/lib/devtool/menuconfig.py
@@ -23,9 +23,6 @@
23import os 23import os
24import bb 24import bb
25import logging 25import logging
26import argparse
27import re
28import glob
29from devtool import setup_tinfoil, parse_recipe, DevtoolError, standard, exec_build_env_command 26from devtool import setup_tinfoil, parse_recipe, DevtoolError, standard, exec_build_env_command
30from devtool import check_workspace_recipe 27from devtool import check_workspace_recipe
31logger = logging.getLogger('devtool') 28logger = logging.getLogger('devtool')
@@ -34,7 +31,6 @@ def menuconfig(args, config, basepath, workspace):
34 """Entry point for the devtool 'menuconfig' subcommand""" 31 """Entry point for the devtool 'menuconfig' subcommand"""
35 32
36 rd = "" 33 rd = ""
37 kconfigpath = ""
38 pn_src = "" 34 pn_src = ""
39 localfilesdir = "" 35 localfilesdir = ""
40 workspace_dir = "" 36 workspace_dir = ""
@@ -51,7 +47,6 @@ def menuconfig(args, config, basepath, workspace):
51 raise DevtoolError("This recipe does not support menuconfig option") 47 raise DevtoolError("This recipe does not support menuconfig option")
52 48
53 workspace_dir = os.path.join(config.workspace_path,'sources') 49 workspace_dir = os.path.join(config.workspace_path,'sources')
54 kconfigpath = rd.getVar('B')
55 pn_src = os.path.join(workspace_dir,pn) 50 pn_src = os.path.join(workspace_dir,pn)
56 51
57 # add check to see if oe_local_files exists or not 52 # add check to see if oe_local_files exists or not
@@ -70,7 +65,7 @@ def menuconfig(args, config, basepath, workspace):
70 logger.info('Launching menuconfig') 65 logger.info('Launching menuconfig')
71 exec_build_env_command(config.init_path, basepath, 'bitbake -c menuconfig %s' % pn, watch=True) 66 exec_build_env_command(config.init_path, basepath, 'bitbake -c menuconfig %s' % pn, watch=True)
72 fragment = os.path.join(localfilesdir, 'devtool-fragment.cfg') 67 fragment = os.path.join(localfilesdir, 'devtool-fragment.cfg')
73 res = standard._create_kconfig_diff(pn_src,rd,fragment) 68 standard._create_kconfig_diff(pn_src,rd,fragment)
74 69
75 return 0 70 return 0
76 71
diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py
index 6674e67267..1fd5947c41 100644
--- a/scripts/lib/devtool/standard.py
+++ b/scripts/lib/devtool/standard.py
@@ -18,11 +18,13 @@ import argparse_oe
18import scriptutils 18import scriptutils
19import errno 19import errno
20import glob 20import glob
21import filecmp
22from collections import OrderedDict 21from collections import OrderedDict
22
23from devtool import exec_build_env_command, setup_tinfoil, check_workspace_recipe, use_external_build, setup_git_repo, recipe_to_append, get_bbclassextend_targets, update_unlockedsigs, check_prerelease_version, check_git_repo_dirty, check_git_repo_op, DevtoolError 23from devtool import exec_build_env_command, setup_tinfoil, check_workspace_recipe, use_external_build, setup_git_repo, recipe_to_append, get_bbclassextend_targets, update_unlockedsigs, check_prerelease_version, check_git_repo_dirty, check_git_repo_op, DevtoolError
24from devtool import parse_recipe 24from devtool import parse_recipe
25 25
26import bb.utils
27
26logger = logging.getLogger('devtool') 28logger = logging.getLogger('devtool')
27 29
28override_branch_prefix = 'devtool-override-' 30override_branch_prefix = 'devtool-override-'
@@ -30,7 +32,8 @@ override_branch_prefix = 'devtool-override-'
30 32
31def add(args, config, basepath, workspace): 33def add(args, config, basepath, workspace):
32 """Entry point for the devtool 'add' subcommand""" 34 """Entry point for the devtool 'add' subcommand"""
33 import bb 35 import bb.data
36 import bb.process
34 import oe.recipeutils 37 import oe.recipeutils
35 38
36 if not args.recipename and not args.srctree and not args.fetch and not args.fetchuri: 39 if not args.recipename and not args.srctree and not args.fetch and not args.fetchuri:
@@ -206,7 +209,7 @@ def add(args, config, basepath, workspace):
206 for fn in os.listdir(tempdir): 209 for fn in os.listdir(tempdir):
207 shutil.move(os.path.join(tempdir, fn), recipedir) 210 shutil.move(os.path.join(tempdir, fn), recipedir)
208 else: 211 else:
209 raise DevtoolError('Command \'%s\' did not create any recipe file:\n%s' % (e.command, e.stdout)) 212 raise DevtoolError(f'Failed to create a recipe file for source {source}')
210 attic_recipe = os.path.join(config.workspace_path, 'attic', recipename, os.path.basename(recipefile)) 213 attic_recipe = os.path.join(config.workspace_path, 'attic', recipename, os.path.basename(recipefile))
211 if os.path.exists(attic_recipe): 214 if os.path.exists(attic_recipe):
212 logger.warning('A modified recipe from a previous invocation exists in %s - you may wish to move this over the top of the new recipe if you had changes in it that you want to continue with' % attic_recipe) 215 logger.warning('A modified recipe from a previous invocation exists in %s - you may wish to move this over the top of the new recipe if you had changes in it that you want to continue with' % attic_recipe)
@@ -305,6 +308,7 @@ def add(args, config, basepath, workspace):
305 308
306def _check_compatible_recipe(pn, d): 309def _check_compatible_recipe(pn, d):
307 """Check if the recipe is supported by devtool""" 310 """Check if the recipe is supported by devtool"""
311 import bb.data
308 if pn == 'perf': 312 if pn == 'perf':
309 raise DevtoolError("The perf recipe does not actually check out " 313 raise DevtoolError("The perf recipe does not actually check out "
310 "source and thus cannot be supported by this tool", 314 "source and thus cannot be supported by this tool",
@@ -374,7 +378,7 @@ def _copy_file(src, dst, dry_run_outdir=None, base_outdir=None):
374 378
375def _git_ls_tree(repodir, treeish='HEAD', recursive=False): 379def _git_ls_tree(repodir, treeish='HEAD', recursive=False):
376 """List contents of a git treeish""" 380 """List contents of a git treeish"""
377 import bb 381 import bb.process
378 cmd = ['git', 'ls-tree', '-z', treeish] 382 cmd = ['git', 'ls-tree', '-z', treeish]
379 if recursive: 383 if recursive:
380 cmd.append('-r') 384 cmd.append('-r')
@@ -387,6 +391,19 @@ def _git_ls_tree(repodir, treeish='HEAD', recursive=False):
387 ret[split[3]] = split[0:3] 391 ret[split[3]] = split[0:3]
388 return ret 392 return ret
389 393
394def _git_modified(repodir):
395 """List the difference between HEAD and the index"""
396 import bb.process
397 cmd = ['git', 'status', '--porcelain']
398 out, _ = bb.process.run(cmd, cwd=repodir)
399 ret = []
400 if out:
401 for line in out.split("\n"):
402 if line and not line.startswith('??'):
403 ret.append(line[3:])
404 return ret
405
406
390def _git_exclude_path(srctree, path): 407def _git_exclude_path(srctree, path):
391 """Return pathspec (list of paths) that excludes certain path""" 408 """Return pathspec (list of paths) that excludes certain path"""
392 # NOTE: "Filtering out" files/paths in this way is not entirely reliable - 409 # NOTE: "Filtering out" files/paths in this way is not entirely reliable -
@@ -414,8 +431,6 @@ def _ls_tree(directory):
414 431
415def extract(args, config, basepath, workspace): 432def extract(args, config, basepath, workspace):
416 """Entry point for the devtool 'extract' subcommand""" 433 """Entry point for the devtool 'extract' subcommand"""
417 import bb
418
419 tinfoil = setup_tinfoil(basepath=basepath, tracking=True) 434 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
420 if not tinfoil: 435 if not tinfoil:
421 # Error already shown 436 # Error already shown
@@ -438,8 +453,6 @@ def extract(args, config, basepath, workspace):
438 453
439def sync(args, config, basepath, workspace): 454def sync(args, config, basepath, workspace):
440 """Entry point for the devtool 'sync' subcommand""" 455 """Entry point for the devtool 'sync' subcommand"""
441 import bb
442
443 tinfoil = setup_tinfoil(basepath=basepath, tracking=True) 456 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
444 if not tinfoil: 457 if not tinfoil:
445 # Error already shown 458 # Error already shown
@@ -460,37 +473,11 @@ def sync(args, config, basepath, workspace):
460 finally: 473 finally:
461 tinfoil.shutdown() 474 tinfoil.shutdown()
462 475
463def symlink_oelocal_files_srctree(rd, srctree):
464 import oe.patch
465 if os.path.abspath(rd.getVar('S')) == os.path.abspath(rd.getVar('WORKDIR')):
466 # If recipe extracts to ${WORKDIR}, symlink the files into the srctree
467 # (otherwise the recipe won't build as expected)
468 local_files_dir = os.path.join(srctree, 'oe-local-files')
469 addfiles = []
470 for root, _, files in os.walk(local_files_dir):
471 relpth = os.path.relpath(root, local_files_dir)
472 if relpth != '.':
473 bb.utils.mkdirhier(os.path.join(srctree, relpth))
474 for fn in files:
475 if fn == '.gitignore':
476 continue
477 destpth = os.path.join(srctree, relpth, fn)
478 if os.path.exists(destpth):
479 os.unlink(destpth)
480 if relpth != '.':
481 back_relpth = os.path.relpath(local_files_dir, root)
482 os.symlink('%s/oe-local-files/%s/%s' % (back_relpth, relpth, fn), destpth)
483 else:
484 os.symlink('oe-local-files/%s' % fn, destpth)
485 addfiles.append(os.path.join(relpth, fn))
486 if addfiles:
487 oe.patch.GitApplyTree.commitIgnored("Add local file symlinks", dir=srctree, files=addfiles, d=rd)
488
489def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, workspace, fixed_setup, d, tinfoil, no_overrides=False): 476def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, workspace, fixed_setup, d, tinfoil, no_overrides=False):
490 """Extract sources of a recipe""" 477 """Extract sources of a recipe"""
491 import oe.recipeutils
492 import oe.patch
493 import oe.path 478 import oe.path
479 import bb.data
480 import bb.process
494 481
495 pn = d.getVar('PN') 482 pn = d.getVar('PN')
496 483
@@ -555,6 +542,7 @@ def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, works
555 tempbasedir = d.getVar('WORKDIR') 542 tempbasedir = d.getVar('WORKDIR')
556 bb.utils.mkdirhier(tempbasedir) 543 bb.utils.mkdirhier(tempbasedir)
557 tempdir = tempfile.mkdtemp(prefix='devtooltmp-', dir=tempbasedir) 544 tempdir = tempfile.mkdtemp(prefix='devtooltmp-', dir=tempbasedir)
545 appendbackup = None
558 try: 546 try:
559 tinfoil.logger.setLevel(logging.WARNING) 547 tinfoil.logger.setLevel(logging.WARNING)
560 548
@@ -565,7 +553,6 @@ def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, works
565 appendbackup = os.path.join(tempdir, os.path.basename(appendfile) + '.bak') 553 appendbackup = os.path.join(tempdir, os.path.basename(appendfile) + '.bak')
566 shutil.copyfile(appendfile, appendbackup) 554 shutil.copyfile(appendfile, appendbackup)
567 else: 555 else:
568 appendbackup = None
569 bb.utils.mkdirhier(os.path.dirname(appendfile)) 556 bb.utils.mkdirhier(os.path.dirname(appendfile))
570 logger.debug('writing append file %s' % appendfile) 557 logger.debug('writing append file %s' % appendfile)
571 with open(appendfile, 'a') as f: 558 with open(appendfile, 'a') as f:
@@ -638,7 +625,7 @@ def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, works
638 srcsubdir = f.read() 625 srcsubdir = f.read()
639 except FileNotFoundError as e: 626 except FileNotFoundError as e:
640 raise DevtoolError('Something went wrong with source extraction - the devtool-source class was not active or did not function correctly:\n%s' % str(e)) 627 raise DevtoolError('Something went wrong with source extraction - the devtool-source class was not active or did not function correctly:\n%s' % str(e))
641 srcsubdir_rel = os.path.relpath(srcsubdir, os.path.join(tempdir, 'workdir')) 628 srcsubdir_rel = os.path.relpath(srcsubdir, os.path.join(tempdir, 'workdir', os.path.relpath(d.getVar('UNPACKDIR'), d.getVar('WORKDIR'))))
642 629
643 # Check if work-shared is empty, if yes 630 # Check if work-shared is empty, if yes
644 # find source and copy to work-shared 631 # find source and copy to work-shared
@@ -657,35 +644,22 @@ def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, works
657 elif not os.path.exists(workshareddir): 644 elif not os.path.exists(workshareddir):
658 oe.path.copyhardlinktree(srcsubdir, workshareddir) 645 oe.path.copyhardlinktree(srcsubdir, workshareddir)
659 646
660 tempdir_localdir = os.path.join(tempdir, 'oe-local-files')
661 srctree_localdir = os.path.join(srctree, 'oe-local-files')
662
663 if sync: 647 if sync:
664 bb.process.run('git fetch file://' + srcsubdir + ' ' + devbranch + ':' + devbranch, cwd=srctree) 648 try:
665 649 logger.info('Backing up current %s branch as branch: %s.bak' % (devbranch, devbranch))
666 # Move the oe-local-files directory to srctree. 650 bb.process.run('git branch -f ' + devbranch + '.bak', cwd=srctree)
667 # As oe-local-files is not part of the constructed git tree, 651
668 # removing it directly during the synchronization might surprise 652 # Use git fetch to update the source with the current recipe
669 # the user. Instead, we move it to oe-local-files.bak and remind 653 # To be able to update the currently checked out branch with
670 # the user in the log message. 654 # possibly new history (no fast-forward) git needs to be told
671 if os.path.exists(srctree_localdir + '.bak'): 655 # that's ok
672 shutil.rmtree(srctree_localdir + '.bak') 656 logger.info('Syncing source files including patches to git branch: %s' % devbranch)
673 657 bb.process.run('git fetch --update-head-ok --force file://' + srcsubdir + ' ' + devbranch + ':' + devbranch, cwd=srctree)
674 if os.path.exists(srctree_localdir): 658 except bb.process.ExecutionError as e:
675 logger.info('Backing up current local file directory %s' % srctree_localdir) 659 raise DevtoolError("Error when syncing source files to local checkout: %s" % str(e))
676 shutil.move(srctree_localdir, srctree_localdir + '.bak')
677
678 if os.path.exists(tempdir_localdir):
679 logger.info('Syncing local source files to srctree...')
680 shutil.copytree(tempdir_localdir, srctree_localdir)
681 else:
682 # Move oe-local-files directory to srctree
683 if os.path.exists(tempdir_localdir):
684 logger.info('Adding local source files to srctree...')
685 shutil.move(tempdir_localdir, srcsubdir)
686 660
661 else:
687 shutil.move(srcsubdir, srctree) 662 shutil.move(srcsubdir, srctree)
688 symlink_oelocal_files_srctree(d, srctree)
689 663
690 if is_kernel_yocto: 664 if is_kernel_yocto:
691 logger.info('Copying kernel config to srctree') 665 logger.info('Copying kernel config to srctree')
@@ -704,8 +678,6 @@ def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, works
704 678
705def _add_md5(config, recipename, filename): 679def _add_md5(config, recipename, filename):
706 """Record checksum of a file (or recursively for a directory) to the md5-file of the workspace""" 680 """Record checksum of a file (or recursively for a directory) to the md5-file of the workspace"""
707 import bb.utils
708
709 def addfile(fn): 681 def addfile(fn):
710 md5 = bb.utils.md5_file(fn) 682 md5 = bb.utils.md5_file(fn)
711 with open(os.path.join(config.workspace_path, '.devtool_md5'), 'a+') as f: 683 with open(os.path.join(config.workspace_path, '.devtool_md5'), 'a+') as f:
@@ -724,7 +696,6 @@ def _add_md5(config, recipename, filename):
724def _check_preserve(config, recipename): 696def _check_preserve(config, recipename):
725 """Check if a file was manually changed and needs to be saved in 'attic' 697 """Check if a file was manually changed and needs to be saved in 'attic'
726 directory""" 698 directory"""
727 import bb.utils
728 origfile = os.path.join(config.workspace_path, '.devtool_md5') 699 origfile = os.path.join(config.workspace_path, '.devtool_md5')
729 newfile = os.path.join(config.workspace_path, '.devtool_md5_new') 700 newfile = os.path.join(config.workspace_path, '.devtool_md5_new')
730 preservepath = os.path.join(config.workspace_path, 'attic', recipename) 701 preservepath = os.path.join(config.workspace_path, 'attic', recipename)
@@ -755,36 +726,36 @@ def _check_preserve(config, recipename):
755 726
756def get_staging_kver(srcdir): 727def get_staging_kver(srcdir):
757 # Kernel version from work-shared 728 # Kernel version from work-shared
758 kerver = [] 729 import itertools
759 staging_kerVer="" 730 try:
760 if os.path.exists(srcdir) and os.listdir(srcdir):
761 with open(os.path.join(srcdir, "Makefile")) as f: 731 with open(os.path.join(srcdir, "Makefile")) as f:
762 version = [next(f) for x in range(5)][1:4] 732 # Take VERSION, PATCHLEVEL, SUBLEVEL from lines 1, 2, 3
763 for word in version: 733 return ".".join(line.rstrip().split('= ')[1] for line in itertools.islice(f, 1, 4))
764 kerver.append(word.split('= ')[1].split('\n')[0]) 734 except FileNotFoundError:
765 staging_kerVer = ".".join(kerver) 735 return ""
766 return staging_kerVer
767 736
768def get_staging_kbranch(srcdir): 737def get_staging_kbranch(srcdir):
738 import bb.process
769 staging_kbranch = "" 739 staging_kbranch = ""
770 if os.path.exists(srcdir) and os.listdir(srcdir): 740 if os.path.exists(srcdir) and os.listdir(srcdir):
771 (branch, _) = bb.process.run('git branch | grep \\* | cut -d \' \' -f2', cwd=srcdir) 741 (branch, _) = bb.process.run('git branch | grep \\* | cut -d \' \' -f2', cwd=srcdir)
772 staging_kbranch = "".join(branch.split('\n')[0]) 742 staging_kbranch = "".join(branch.split('\n')[0])
773 return staging_kbranch 743 return staging_kbranch
774 744
775def get_real_srctree(srctree, s, workdir): 745def get_real_srctree(srctree, s, unpackdir):
776 # Check that recipe isn't using a shared workdir 746 # Check that recipe isn't using a shared workdir
777 s = os.path.abspath(s) 747 s = os.path.abspath(s)
778 workdir = os.path.abspath(workdir) 748 unpackdir = os.path.abspath(unpackdir)
779 if s.startswith(workdir) and s != workdir and os.path.dirname(s) != workdir: 749 if s.startswith(unpackdir) and s != unpackdir and os.path.dirname(s) != unpackdir:
780 # Handle if S is set to a subdirectory of the source 750 # Handle if S is set to a subdirectory of the source
781 srcsubdir = os.path.relpath(s, workdir).split(os.sep, 1)[1] 751 srcsubdir = os.path.relpath(s, unpackdir).split(os.sep, 1)[1]
782 srctree = os.path.join(srctree, srcsubdir) 752 srctree = os.path.join(srctree, srcsubdir)
783 return srctree 753 return srctree
784 754
785def modify(args, config, basepath, workspace): 755def modify(args, config, basepath, workspace):
786 """Entry point for the devtool 'modify' subcommand""" 756 """Entry point for the devtool 'modify' subcommand"""
787 import bb 757 import bb.data
758 import bb.process
788 import oe.recipeutils 759 import oe.recipeutils
789 import oe.patch 760 import oe.patch
790 import oe.path 761 import oe.path
@@ -840,35 +811,21 @@ def modify(args, config, basepath, workspace):
840 staging_kbranch = get_staging_kbranch(srcdir) 811 staging_kbranch = get_staging_kbranch(srcdir)
841 if (os.path.exists(srcdir) and os.listdir(srcdir)) and (kernelVersion in staging_kerVer and staging_kbranch == kbranch): 812 if (os.path.exists(srcdir) and os.listdir(srcdir)) and (kernelVersion in staging_kerVer and staging_kbranch == kbranch):
842 oe.path.copyhardlinktree(srcdir, srctree) 813 oe.path.copyhardlinktree(srcdir, srctree)
843 workdir = rd.getVar('WORKDIR') 814 unpackdir = rd.getVar('UNPACKDIR')
844 srcsubdir = rd.getVar('S') 815 srcsubdir = rd.getVar('S')
845 localfilesdir = os.path.join(srctree, 'oe-local-files')
846 # Move local source files into separate subdir
847 recipe_patches = [os.path.basename(patch) for patch in oe.recipeutils.get_recipe_patches(rd)]
848 local_files = oe.recipeutils.get_recipe_local_files(rd)
849 816
850 for key in local_files.copy(): 817 # Add locally copied files to gitignore as we add back to the metadata directly
851 if key.endswith('scc'): 818 local_files = oe.recipeutils.get_recipe_local_files(rd)
852 sccfile = open(local_files[key], 'r')
853 for l in sccfile:
854 line = l.split()
855 if line and line[0] in ('kconf', 'patch'):
856 cfg = os.path.join(os.path.dirname(local_files[key]), line[-1])
857 if not cfg in local_files.values():
858 local_files[line[-1]] = cfg
859 shutil.copy2(cfg, workdir)
860 sccfile.close()
861
862 # Ignore local files with subdir={BP}
863 srcabspath = os.path.abspath(srcsubdir) 819 srcabspath = os.path.abspath(srcsubdir)
864 local_files = [fname for fname in local_files if os.path.exists(os.path.join(workdir, fname)) and (srcabspath == workdir or not os.path.join(workdir, fname).startswith(srcabspath + os.sep))] 820 local_files = [fname for fname in local_files if
821 os.path.exists(os.path.join(unpackdir, fname)) and
822 srcabspath == unpackdir]
865 if local_files: 823 if local_files:
866 for fname in local_files: 824 with open(os.path.join(srctree, '.gitignore'), 'a+') as f:
867 _move_file(os.path.join(workdir, fname), os.path.join(srctree, 'oe-local-files', fname)) 825 f.write('# Ignore local files, by default. Remove following lines'
868 with open(os.path.join(srctree, 'oe-local-files', '.gitignore'), 'w') as f: 826 'if you want to commit the directory to Git\n')
869 f.write('# Ignore local files, by default. Remove this file if you want to commit the directory to Git\n*\n') 827 for fname in local_files:
870 828 f.write('%s\n' % fname)
871 symlink_oelocal_files_srctree(rd, srctree)
872 829
873 task = 'do_configure' 830 task = 'do_configure'
874 res = tinfoil.build_targets(pn, task, handle_events=True) 831 res = tinfoil.build_targets(pn, task, handle_events=True)
@@ -893,7 +850,10 @@ def modify(args, config, basepath, workspace):
893 (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_revs["."], cwd=srctree) 850 (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_revs["."], cwd=srctree)
894 commits["."] = stdout.split() 851 commits["."] = stdout.split()
895 check_commits = True 852 check_commits = True
896 (stdout, _) = bb.process.run('git submodule --quiet foreach --recursive \'echo `git rev-parse devtool-base` $PWD\'', cwd=srctree) 853 try:
854 (stdout, _) = bb.process.run('git submodule --quiet foreach --recursive \'echo `git rev-parse devtool-base` $PWD\'', cwd=srctree)
855 except bb.process.ExecutionError:
856 stdout = ""
897 for line in stdout.splitlines(): 857 for line in stdout.splitlines():
898 (rev, submodule_path) = line.split() 858 (rev, submodule_path) = line.split()
899 submodule = os.path.relpath(submodule_path, srctree) 859 submodule = os.path.relpath(submodule_path, srctree)
@@ -947,7 +907,7 @@ def modify(args, config, basepath, workspace):
947 907
948 # Need to grab this here in case the source is within a subdirectory 908 # Need to grab this here in case the source is within a subdirectory
949 srctreebase = srctree 909 srctreebase = srctree
950 srctree = get_real_srctree(srctree, rd.getVar('S'), rd.getVar('WORKDIR')) 910 srctree = get_real_srctree(srctree, rd.getVar('S'), rd.getVar('UNPACKDIR'))
951 911
952 bb.utils.mkdirhier(os.path.dirname(appendfile)) 912 bb.utils.mkdirhier(os.path.dirname(appendfile))
953 with open(appendfile, 'w') as f: 913 with open(appendfile, 'w') as f:
@@ -987,13 +947,6 @@ def modify(args, config, basepath, workspace):
987 f.write('EXTERNALSRC_BUILD:pn-%s = "%s"\n' % (pn, srctree)) 947 f.write('EXTERNALSRC_BUILD:pn-%s = "%s"\n' % (pn, srctree))
988 948
989 if bb.data.inherits_class('kernel', rd): 949 if bb.data.inherits_class('kernel', rd):
990 f.write('SRCTREECOVEREDTASKS = "do_validate_branches do_kernel_checkout '
991 'do_fetch do_unpack do_kernel_configcheck"\n')
992 f.write('\ndo_patch[noexec] = "1"\n')
993 f.write('\ndo_configure:append() {\n'
994 ' cp ${B}/.config ${S}/.config.baseline\n'
995 ' ln -sfT ${B}/.config ${S}/.config.new\n'
996 '}\n')
997 f.write('\ndo_kernel_configme:prepend() {\n' 950 f.write('\ndo_kernel_configme:prepend() {\n'
998 ' if [ -e ${S}/.config ]; then\n' 951 ' if [ -e ${S}/.config ]; then\n'
999 ' mv ${S}/.config ${S}/.config.old\n' 952 ' mv ${S}/.config ${S}/.config.old\n'
@@ -1017,6 +970,8 @@ def modify(args, config, basepath, workspace):
1017 if branch == args.branch: 970 if branch == args.branch:
1018 continue 971 continue
1019 f.write('# patches_%s: %s\n' % (branch, ','.join(branch_patches[branch]))) 972 f.write('# patches_%s: %s\n' % (branch, ','.join(branch_patches[branch])))
973 if args.debug_build:
974 f.write('\nDEBUG_BUILD = "1"\n')
1020 975
1021 update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn]) 976 update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn])
1022 977
@@ -1061,6 +1016,7 @@ def rename(args, config, basepath, workspace):
1061 origfnver = '' 1016 origfnver = ''
1062 1017
1063 recipefilemd5 = None 1018 recipefilemd5 = None
1019 newrecipefilemd5 = None
1064 tinfoil = setup_tinfoil(basepath=basepath, tracking=True) 1020 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
1065 try: 1021 try:
1066 rd = parse_recipe(config, tinfoil, args.recipename, True) 1022 rd = parse_recipe(config, tinfoil, args.recipename, True)
@@ -1138,6 +1094,7 @@ def rename(args, config, basepath, workspace):
1138 1094
1139 # Rename source tree if it's the default path 1095 # Rename source tree if it's the default path
1140 appendmd5 = None 1096 appendmd5 = None
1097 newappendmd5 = None
1141 if not args.no_srctree: 1098 if not args.no_srctree:
1142 srctree = workspace[args.recipename]['srctree'] 1099 srctree = workspace[args.recipename]['srctree']
1143 if os.path.abspath(srctree) == os.path.join(config.workspace_path, 'sources', args.recipename): 1100 if os.path.abspath(srctree) == os.path.join(config.workspace_path, 'sources', args.recipename):
@@ -1226,7 +1183,7 @@ def _get_patchset_revs(srctree, recipe_path, initial_rev=None, force_patch_refre
1226 """Get initial and update rev of a recipe. These are the start point of the 1183 """Get initial and update rev of a recipe. These are the start point of the
1227 whole patchset and start point for the patches to be re-generated/updated. 1184 whole patchset and start point for the patches to be re-generated/updated.
1228 """ 1185 """
1229 import bb 1186 import bb.process
1230 1187
1231 # Get current branch 1188 # Get current branch
1232 stdout, _ = bb.process.run('git rev-parse --abbrev-ref HEAD', 1189 stdout, _ = bb.process.run('git rev-parse --abbrev-ref HEAD',
@@ -1352,6 +1309,7 @@ def _export_patches(srctree, rd, start_revs, destdir, changed_revs=None):
1352 """ 1309 """
1353 import oe.recipeutils 1310 import oe.recipeutils
1354 from oe.patch import GitApplyTree 1311 from oe.patch import GitApplyTree
1312 import bb.process
1355 updated = OrderedDict() 1313 updated = OrderedDict()
1356 added = OrderedDict() 1314 added = OrderedDict()
1357 seqpatch_re = re.compile('^([0-9]{4}-)?(.+)') 1315 seqpatch_re = re.compile('^([0-9]{4}-)?(.+)')
@@ -1373,6 +1331,7 @@ def _export_patches(srctree, rd, start_revs, destdir, changed_revs=None):
1373 # values, but they ought to be anyway... 1331 # values, but they ought to be anyway...
1374 new_basename = seqpatch_re.match(new_patch).group(2) 1332 new_basename = seqpatch_re.match(new_patch).group(2)
1375 match_name = None 1333 match_name = None
1334 old_patch = None
1376 for old_patch in existing_patches: 1335 for old_patch in existing_patches:
1377 old_basename = seqpatch_re.match(old_patch).group(2) 1336 old_basename = seqpatch_re.match(old_patch).group(2)
1378 old_basename_splitext = os.path.splitext(old_basename) 1337 old_basename_splitext = os.path.splitext(old_basename)
@@ -1421,6 +1380,7 @@ def _export_patches(srctree, rd, start_revs, destdir, changed_revs=None):
1421 1380
1422def _create_kconfig_diff(srctree, rd, outfile): 1381def _create_kconfig_diff(srctree, rd, outfile):
1423 """Create a kconfig fragment""" 1382 """Create a kconfig fragment"""
1383 import bb.process
1424 # Only update config fragment if both config files exist 1384 # Only update config fragment if both config files exist
1425 orig_config = os.path.join(srctree, '.config.baseline') 1385 orig_config = os.path.join(srctree, '.config.baseline')
1426 new_config = os.path.join(srctree, '.config.new') 1386 new_config = os.path.join(srctree, '.config.new')
@@ -1452,16 +1412,21 @@ def _export_local_files(srctree, rd, destdir, srctreebase):
1452 1. updated - files that already exist in SRCURI 1412 1. updated - files that already exist in SRCURI
1453 2. added - new files files that don't exist in SRCURI 1413 2. added - new files files that don't exist in SRCURI
1454 3 removed - files that exist in SRCURI but not in exported files 1414 3 removed - files that exist in SRCURI but not in exported files
1455 In each dict the key is the 'basepath' of the URI and value is the 1415 In each dict the key is the 'basepath' of the URI and value is:
1456 absolute path to the existing file in recipe space (if any). 1416 - for updated and added dicts, a dict with 1 optionnal key:
1417 - 'path': the absolute path to the existing file in recipe space (if any)
1418 - for removed dict, the absolute path to the existing file in recipe space
1457 """ 1419 """
1458 import oe.recipeutils 1420 import oe.recipeutils
1421 import bb.data
1422 import bb.process
1459 1423
1460 # Find out local files (SRC_URI files that exist in the "recipe space"). 1424 # Find out local files (SRC_URI files that exist in the "recipe space").
1461 # Local files that reside in srctree are not included in patch generation. 1425 # Local files that reside in srctree are not included in patch generation.
1462 # Instead they are directly copied over the original source files (in 1426 # Instead they are directly copied over the original source files (in
1463 # recipe space). 1427 # recipe space).
1464 existing_files = oe.recipeutils.get_recipe_local_files(rd) 1428 existing_files = oe.recipeutils.get_recipe_local_files(rd)
1429
1465 new_set = None 1430 new_set = None
1466 updated = OrderedDict() 1431 updated = OrderedDict()
1467 added = OrderedDict() 1432 added = OrderedDict()
@@ -1478,24 +1443,28 @@ def _export_local_files(srctree, rd, destdir, srctreebase):
1478 if branchname.startswith(override_branch_prefix): 1443 if branchname.startswith(override_branch_prefix):
1479 return (updated, added, removed) 1444 return (updated, added, removed)
1480 1445
1481 local_files_dir = os.path.join(srctreebase, 'oe-local-files') 1446 files = _git_modified(srctree)
1482 git_files = _git_ls_tree(srctree) 1447 #if not files:
1483 if 'oe-local-files' in git_files: 1448 # files = _ls_tree(srctree)
1484 # If tracked by Git, take the files from srctree HEAD. First get 1449 for f in files:
1485 # the tree object of the directory 1450 fullfile = os.path.join(srctree, f)
1486 tmp_index = os.path.join(srctree, '.git', 'index.tmp.devtool') 1451 if os.path.exists(os.path.join(fullfile, ".git")):
1487 tree = git_files['oe-local-files'][2] 1452 # submodules handled elsewhere
1488 bb.process.run(['git', 'checkout', tree, '--', '.'], cwd=srctree, 1453 continue
1489 env=dict(os.environ, GIT_WORK_TREE=destdir, 1454 if f not in existing_files:
1490 GIT_INDEX_FILE=tmp_index)) 1455 added[f] = {}
1491 new_set = list(_git_ls_tree(srctree, tree, True).keys()) 1456 if os.path.isdir(os.path.join(srctree, f)):
1492 elif os.path.isdir(local_files_dir): 1457 shutil.copytree(fullfile, os.path.join(destdir, f))
1493 # If not tracked by Git, just copy from working copy 1458 else:
1494 new_set = _ls_tree(local_files_dir) 1459 shutil.copy2(fullfile, os.path.join(destdir, f))
1495 bb.process.run(['cp', '-ax', 1460 elif not os.path.exists(fullfile):
1496 os.path.join(local_files_dir, '.'), destdir]) 1461 removed[f] = existing_files[f]
1497 else: 1462 elif f in existing_files:
1498 new_set = [] 1463 updated[f] = {'path' : existing_files[f]}
1464 if os.path.isdir(os.path.join(srctree, f)):
1465 shutil.copytree(fullfile, os.path.join(destdir, f))
1466 else:
1467 shutil.copy2(fullfile, os.path.join(destdir, f))
1499 1468
1500 # Special handling for kernel config 1469 # Special handling for kernel config
1501 if bb.data.inherits_class('kernel-yocto', rd): 1470 if bb.data.inherits_class('kernel-yocto', rd):
@@ -1503,17 +1472,14 @@ def _export_local_files(srctree, rd, destdir, srctreebase):
1503 fragment_path = os.path.join(destdir, fragment_fn) 1472 fragment_path = os.path.join(destdir, fragment_fn)
1504 if _create_kconfig_diff(srctree, rd, fragment_path): 1473 if _create_kconfig_diff(srctree, rd, fragment_path):
1505 if os.path.exists(fragment_path): 1474 if os.path.exists(fragment_path):
1506 if fragment_fn not in new_set: 1475 if fragment_fn in removed:
1507 new_set.append(fragment_fn) 1476 del removed[fragment_fn]
1508 # Copy fragment to local-files 1477 if fragment_fn not in updated and fragment_fn not in added:
1509 if os.path.isdir(local_files_dir): 1478 added[fragment_fn] = {}
1510 shutil.copy2(fragment_path, local_files_dir)
1511 else: 1479 else:
1512 if fragment_fn in new_set: 1480 if fragment_fn in updated:
1513 new_set.remove(fragment_fn) 1481 removed[fragment_fn] = updated[fragment_fn]
1514 # Remove fragment from local-files 1482 del updated[fragment_fn]
1515 if os.path.exists(os.path.join(local_files_dir, fragment_fn)):
1516 os.unlink(os.path.join(local_files_dir, fragment_fn))
1517 1483
1518 # Special handling for cml1, ccmake, etc bbclasses that generated 1484 # Special handling for cml1, ccmake, etc bbclasses that generated
1519 # configuration fragment files that are consumed as source files 1485 # configuration fragment files that are consumed as source files
@@ -1521,42 +1487,13 @@ def _export_local_files(srctree, rd, destdir, srctreebase):
1521 if bb.data.inherits_class(frag_class, rd): 1487 if bb.data.inherits_class(frag_class, rd):
1522 srcpath = os.path.join(rd.getVar('WORKDIR'), frag_name) 1488 srcpath = os.path.join(rd.getVar('WORKDIR'), frag_name)
1523 if os.path.exists(srcpath): 1489 if os.path.exists(srcpath):
1524 if frag_name not in new_set: 1490 if frag_name in removed:
1525 new_set.append(frag_name) 1491 del removed[frag_name]
1492 if frag_name not in updated:
1493 added[frag_name] = {}
1526 # copy fragment into destdir 1494 # copy fragment into destdir
1527 shutil.copy2(srcpath, destdir) 1495 shutil.copy2(srcpath, destdir)
1528 # copy fragment into local files if exists 1496
1529 if os.path.isdir(local_files_dir):
1530 shutil.copy2(srcpath, local_files_dir)
1531
1532 if new_set is not None:
1533 for fname in new_set:
1534 if fname in existing_files:
1535 origpath = existing_files.pop(fname)
1536 workpath = os.path.join(local_files_dir, fname)
1537 if not filecmp.cmp(origpath, workpath):
1538 updated[fname] = origpath
1539 elif fname != '.gitignore':
1540 added[fname] = None
1541
1542 workdir = rd.getVar('WORKDIR')
1543 s = rd.getVar('S')
1544 if not s.endswith(os.sep):
1545 s += os.sep
1546
1547 if workdir != s:
1548 # Handle files where subdir= was specified
1549 for fname in list(existing_files.keys()):
1550 # FIXME handle both subdir starting with BP and not?
1551 fworkpath = os.path.join(workdir, fname)
1552 if fworkpath.startswith(s):
1553 fpath = os.path.join(srctree, os.path.relpath(fworkpath, s))
1554 if os.path.exists(fpath):
1555 origpath = existing_files.pop(fname)
1556 if not filecmp.cmp(origpath, fpath):
1557 updated[fpath] = origpath
1558
1559 removed = existing_files
1560 return (updated, added, removed) 1497 return (updated, added, removed)
1561 1498
1562 1499
@@ -1574,7 +1511,7 @@ def _determine_files_dir(rd):
1574 1511
1575def _update_recipe_srcrev(recipename, workspace, srctree, rd, appendlayerdir, wildcard_version, no_remove, no_report_remove, dry_run_outdir=None): 1512def _update_recipe_srcrev(recipename, workspace, srctree, rd, appendlayerdir, wildcard_version, no_remove, no_report_remove, dry_run_outdir=None):
1576 """Implement the 'srcrev' mode of update-recipe""" 1513 """Implement the 'srcrev' mode of update-recipe"""
1577 import bb 1514 import bb.process
1578 import oe.recipeutils 1515 import oe.recipeutils
1579 1516
1580 dry_run_suffix = ' (dry-run)' if dry_run_outdir else '' 1517 dry_run_suffix = ' (dry-run)' if dry_run_outdir else ''
@@ -1612,6 +1549,7 @@ def _update_recipe_srcrev(recipename, workspace, srctree, rd, appendlayerdir, wi
1612 local_files_dir = tempfile.mkdtemp(dir=tempdir) 1549 local_files_dir = tempfile.mkdtemp(dir=tempdir)
1613 srctreebase = workspace[recipename]['srctreebase'] 1550 srctreebase = workspace[recipename]['srctreebase']
1614 upd_f, new_f, del_f = _export_local_files(srctree, rd, local_files_dir, srctreebase) 1551 upd_f, new_f, del_f = _export_local_files(srctree, rd, local_files_dir, srctreebase)
1552 removedentries = {}
1615 if not no_remove: 1553 if not no_remove:
1616 # Find list of existing patches in recipe file 1554 # Find list of existing patches in recipe file
1617 patches_dir = tempfile.mkdtemp(dir=tempdir) 1555 patches_dir = tempfile.mkdtemp(dir=tempdir)
@@ -1640,7 +1578,8 @@ def _update_recipe_srcrev(recipename, workspace, srctree, rd, appendlayerdir, wi
1640 redirect_output=dry_run_outdir) 1578 redirect_output=dry_run_outdir)
1641 else: 1579 else:
1642 files_dir = _determine_files_dir(rd) 1580 files_dir = _determine_files_dir(rd)
1643 for basepath, path in upd_f.items(): 1581 for basepath, param in upd_f.items():
1582 path = param['path']
1644 logger.info('Updating file %s%s' % (basepath, dry_run_suffix)) 1583 logger.info('Updating file %s%s' % (basepath, dry_run_suffix))
1645 if os.path.isabs(basepath): 1584 if os.path.isabs(basepath):
1646 # Original file (probably with subdir pointing inside source tree) 1585 # Original file (probably with subdir pointing inside source tree)
@@ -1650,7 +1589,8 @@ def _update_recipe_srcrev(recipename, workspace, srctree, rd, appendlayerdir, wi
1650 _move_file(os.path.join(local_files_dir, basepath), path, 1589 _move_file(os.path.join(local_files_dir, basepath), path,
1651 dry_run_outdir=dry_run_outdir, base_outdir=recipedir) 1590 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
1652 update_srcuri= True 1591 update_srcuri= True
1653 for basepath, path in new_f.items(): 1592 for basepath, param in new_f.items():
1593 path = param['path']
1654 logger.info('Adding new file %s%s' % (basepath, dry_run_suffix)) 1594 logger.info('Adding new file %s%s' % (basepath, dry_run_suffix))
1655 _move_file(os.path.join(local_files_dir, basepath), 1595 _move_file(os.path.join(local_files_dir, basepath),
1656 os.path.join(files_dir, basepath), 1596 os.path.join(files_dir, basepath),
@@ -1673,7 +1613,6 @@ def _update_recipe_srcrev(recipename, workspace, srctree, rd, appendlayerdir, wi
1673 1613
1674def _update_recipe_patch(recipename, workspace, srctree, rd, appendlayerdir, wildcard_version, no_remove, no_report_remove, initial_rev, dry_run_outdir=None, force_patch_refresh=False): 1614def _update_recipe_patch(recipename, workspace, srctree, rd, appendlayerdir, wildcard_version, no_remove, no_report_remove, initial_rev, dry_run_outdir=None, force_patch_refresh=False):
1675 """Implement the 'patch' mode of update-recipe""" 1615 """Implement the 'patch' mode of update-recipe"""
1676 import bb
1677 import oe.recipeutils 1616 import oe.recipeutils
1678 1617
1679 recipefile = rd.getVar('FILE') 1618 recipefile = rd.getVar('FILE')
@@ -1772,7 +1711,8 @@ def _update_recipe_patch(recipename, workspace, srctree, rd, appendlayerdir, wil
1772 else: 1711 else:
1773 # Update existing files 1712 # Update existing files
1774 files_dir = _determine_files_dir(rd) 1713 files_dir = _determine_files_dir(rd)
1775 for basepath, path in upd_f.items(): 1714 for basepath, param in upd_f.items():
1715 path = param['path']
1776 logger.info('Updating file %s' % basepath) 1716 logger.info('Updating file %s' % basepath)
1777 if os.path.isabs(basepath): 1717 if os.path.isabs(basepath):
1778 # Original file (probably with subdir pointing inside source tree) 1718 # Original file (probably with subdir pointing inside source tree)
@@ -1786,6 +1726,7 @@ def _update_recipe_patch(recipename, workspace, srctree, rd, appendlayerdir, wil
1786 for basepath, param in upd_p.items(): 1726 for basepath, param in upd_p.items():
1787 path = param['path'] 1727 path = param['path']
1788 patchdir = param.get('patchdir', ".") 1728 patchdir = param.get('patchdir', ".")
1729 patchdir_param = {}
1789 if patchdir != "." : 1730 if patchdir != "." :
1790 patchdir_param = dict(patchdir_params) 1731 patchdir_param = dict(patchdir_params)
1791 if patchdir_param: 1732 if patchdir_param:
@@ -1806,7 +1747,7 @@ def _update_recipe_patch(recipename, workspace, srctree, rd, appendlayerdir, wil
1806 dry_run_outdir=dry_run_outdir, base_outdir=recipedir) 1747 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
1807 updatefiles = True 1748 updatefiles = True
1808 # Add any new files 1749 # Add any new files
1809 for basepath, path in new_f.items(): 1750 for basepath, param in new_f.items():
1810 logger.info('Adding new file %s%s' % (basepath, dry_run_suffix)) 1751 logger.info('Adding new file %s%s' % (basepath, dry_run_suffix))
1811 _move_file(os.path.join(local_files_dir, basepath), 1752 _move_file(os.path.join(local_files_dir, basepath),
1812 os.path.join(files_dir, basepath), 1753 os.path.join(files_dir, basepath),
@@ -1851,6 +1792,7 @@ def _update_recipe_patch(recipename, workspace, srctree, rd, appendlayerdir, wil
1851 1792
1852def _guess_recipe_update_mode(srctree, rdata): 1793def _guess_recipe_update_mode(srctree, rdata):
1853 """Guess the recipe update mode to use""" 1794 """Guess the recipe update mode to use"""
1795 import bb.process
1854 src_uri = (rdata.getVar('SRC_URI') or '').split() 1796 src_uri = (rdata.getVar('SRC_URI') or '').split()
1855 git_uris = [uri for uri in src_uri if uri.startswith('git://')] 1797 git_uris = [uri for uri in src_uri if uri.startswith('git://')]
1856 if not git_uris: 1798 if not git_uris:
@@ -1872,6 +1814,8 @@ def _guess_recipe_update_mode(srctree, rdata):
1872 return 'patch' 1814 return 'patch'
1873 1815
1874def _update_recipe(recipename, workspace, rd, mode, appendlayerdir, wildcard_version, no_remove, initial_rev, no_report_remove=False, dry_run_outdir=None, no_overrides=False, force_patch_refresh=False): 1816def _update_recipe(recipename, workspace, rd, mode, appendlayerdir, wildcard_version, no_remove, initial_rev, no_report_remove=False, dry_run_outdir=None, no_overrides=False, force_patch_refresh=False):
1817 import bb.data
1818 import bb.process
1875 srctree = workspace[recipename]['srctree'] 1819 srctree = workspace[recipename]['srctree']
1876 if mode == 'auto': 1820 if mode == 'auto':
1877 mode = _guess_recipe_update_mode(srctree, rd) 1821 mode = _guess_recipe_update_mode(srctree, rd)
@@ -1994,6 +1938,7 @@ def status(args, config, basepath, workspace):
1994 1938
1995def _reset(recipes, no_clean, remove_work, config, basepath, workspace): 1939def _reset(recipes, no_clean, remove_work, config, basepath, workspace):
1996 """Reset one or more recipes""" 1940 """Reset one or more recipes"""
1941 import bb.process
1997 import oe.path 1942 import oe.path
1998 1943
1999 def clean_preferred_provider(pn, layerconf_path): 1944 def clean_preferred_provider(pn, layerconf_path):
@@ -2006,7 +1951,7 @@ def _reset(recipes, no_clean, remove_work, config, basepath, workspace):
2006 lines = f.readlines() 1951 lines = f.readlines()
2007 with open(new_layerconf_file, 'a') as nf: 1952 with open(new_layerconf_file, 'a') as nf:
2008 for line in lines: 1953 for line in lines:
2009 pprovider_exp = r'^PREFERRED_PROVIDER_.*? = "' + pn + r'"$' 1954 pprovider_exp = r'^PREFERRED_PROVIDER_.*? = "' + re.escape(pn) + r'"$'
2010 if not re.match(pprovider_exp, line): 1955 if not re.match(pprovider_exp, line):
2011 nf.write(line) 1956 nf.write(line)
2012 else: 1957 else:
@@ -2097,8 +2042,6 @@ def _reset(recipes, no_clean, remove_work, config, basepath, workspace):
2097 2042
2098def reset(args, config, basepath, workspace): 2043def reset(args, config, basepath, workspace):
2099 """Entry point for the devtool 'reset' subcommand""" 2044 """Entry point for the devtool 'reset' subcommand"""
2100 import bb
2101 import shutil
2102 2045
2103 recipes = "" 2046 recipes = ""
2104 2047
@@ -2377,6 +2320,7 @@ def register_commands(subparsers, context):
2377 parser_modify.add_argument('--branch', '-b', default="devtool", help='Name for development branch to checkout (when not using -n/--no-extract) (default "%(default)s")') 2320 parser_modify.add_argument('--branch', '-b', default="devtool", help='Name for development branch to checkout (when not using -n/--no-extract) (default "%(default)s")')
2378 parser_modify.add_argument('--no-overrides', '-O', action="store_true", help='Do not create branches for other override configurations') 2321 parser_modify.add_argument('--no-overrides', '-O', action="store_true", help='Do not create branches for other override configurations')
2379 parser_modify.add_argument('--keep-temp', help='Keep temporary directory (for debugging)', action="store_true") 2322 parser_modify.add_argument('--keep-temp', help='Keep temporary directory (for debugging)', action="store_true")
2323 parser_modify.add_argument('--debug-build', action="store_true", help='Add DEBUG_BUILD = "1" to the modified recipe')
2380 parser_modify.set_defaults(func=modify, fixed_setup=context.fixed_setup) 2324 parser_modify.set_defaults(func=modify, fixed_setup=context.fixed_setup)
2381 2325
2382 parser_extract = subparsers.add_parser('extract', help='Extract the source for an existing recipe', 2326 parser_extract = subparsers.add_parser('extract', help='Extract the source for an existing recipe',
diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
index fa5b8ef3c7..d9aca6e2db 100644
--- a/scripts/lib/devtool/upgrade.py
+++ b/scripts/lib/devtool/upgrade.py
@@ -32,7 +32,7 @@ def _run(cmd, cwd=''):
32 32
33def _get_srctree(tmpdir): 33def _get_srctree(tmpdir):
34 srctree = tmpdir 34 srctree = tmpdir
35 dirs = scriptutils.filter_src_subdirs(tmpdir) 35 dirs = os.listdir(tmpdir)
36 if len(dirs) == 1: 36 if len(dirs) == 1:
37 srctree = os.path.join(tmpdir, dirs[0]) 37 srctree = os.path.join(tmpdir, dirs[0])
38 else: 38 else:
@@ -76,19 +76,19 @@ def _rename_recipe_dirs(oldpv, newpv, path):
76 bb.utils.rename(os.path.join(path, oldfile), 76 bb.utils.rename(os.path.join(path, oldfile),
77 os.path.join(path, newfile)) 77 os.path.join(path, newfile))
78 78
79def _rename_recipe_file(oldrecipe, bpn, oldpv, newpv, path): 79def _rename_recipe_file(oldrecipe, pn, oldpv, newpv, path):
80 oldrecipe = os.path.basename(oldrecipe) 80 oldrecipe = os.path.basename(oldrecipe)
81 if oldrecipe.endswith('_%s.bb' % oldpv): 81 if oldrecipe.endswith('_%s.bb' % oldpv):
82 newrecipe = '%s_%s.bb' % (bpn, newpv) 82 newrecipe = '%s_%s.bb' % (pn, newpv)
83 if oldrecipe != newrecipe: 83 if oldrecipe != newrecipe:
84 shutil.move(os.path.join(path, oldrecipe), os.path.join(path, newrecipe)) 84 shutil.move(os.path.join(path, oldrecipe), os.path.join(path, newrecipe))
85 else: 85 else:
86 newrecipe = oldrecipe 86 newrecipe = oldrecipe
87 return os.path.join(path, newrecipe) 87 return os.path.join(path, newrecipe)
88 88
89def _rename_recipe_files(oldrecipe, bpn, oldpv, newpv, path): 89def _rename_recipe_files(oldrecipe, pn, oldpv, newpv, path):
90 _rename_recipe_dirs(oldpv, newpv, path) 90 _rename_recipe_dirs(oldpv, newpv, path)
91 return _rename_recipe_file(oldrecipe, bpn, oldpv, newpv, path) 91 return _rename_recipe_file(oldrecipe, pn, oldpv, newpv, path)
92 92
93def _write_append(rc, srctreebase, srctree, same_dir, no_same_dir, revs, copied, workspace, d): 93def _write_append(rc, srctreebase, srctree, same_dir, no_same_dir, revs, copied, workspace, d):
94 """Writes an append file""" 94 """Writes an append file"""
@@ -169,6 +169,7 @@ def _get_uri(rd):
169 169
170def _extract_new_source(newpv, srctree, no_patch, srcrev, srcbranch, branch, keep_temp, tinfoil, rd): 170def _extract_new_source(newpv, srctree, no_patch, srcrev, srcbranch, branch, keep_temp, tinfoil, rd):
171 """Extract sources of a recipe with a new version""" 171 """Extract sources of a recipe with a new version"""
172 import oe.patch
172 173
173 def __run(cmd): 174 def __run(cmd):
174 """Simple wrapper which calls _run with srctree as cwd""" 175 """Simple wrapper which calls _run with srctree as cwd"""
@@ -187,9 +188,9 @@ def _extract_new_source(newpv, srctree, no_patch, srcrev, srcbranch, branch, kee
187 if uri.startswith('git://') or uri.startswith('gitsm://'): 188 if uri.startswith('git://') or uri.startswith('gitsm://'):
188 __run('git fetch') 189 __run('git fetch')
189 __run('git checkout %s' % rev) 190 __run('git checkout %s' % rev)
190 __run('git tag -f devtool-base-new') 191 __run('git tag -f --no-sign devtool-base-new')
191 __run('git submodule update --recursive') 192 __run('git submodule update --recursive')
192 __run('git submodule foreach \'git tag -f devtool-base-new\'') 193 __run('git submodule foreach \'git tag -f --no-sign devtool-base-new\'')
193 (stdout, _) = __run('git submodule --quiet foreach \'echo $sm_path\'') 194 (stdout, _) = __run('git submodule --quiet foreach \'echo $sm_path\'')
194 paths += [os.path.join(srctree, p) for p in stdout.splitlines()] 195 paths += [os.path.join(srctree, p) for p in stdout.splitlines()]
195 checksums = {} 196 checksums = {}
@@ -256,7 +257,7 @@ def _extract_new_source(newpv, srctree, no_patch, srcrev, srcbranch, branch, kee
256 useroptions = [] 257 useroptions = []
257 oe.patch.GitApplyTree.gitCommandUserOptions(useroptions, d=rd) 258 oe.patch.GitApplyTree.gitCommandUserOptions(useroptions, d=rd)
258 __run('git %s commit -q -m "Commit of upstream changes at version %s" --allow-empty' % (' '.join(useroptions), newpv)) 259 __run('git %s commit -q -m "Commit of upstream changes at version %s" --allow-empty' % (' '.join(useroptions), newpv))
259 __run('git tag -f devtool-base-%s' % newpv) 260 __run('git tag -f --no-sign devtool-base-%s' % newpv)
260 261
261 revs = {} 262 revs = {}
262 for path in paths: 263 for path in paths:
@@ -335,19 +336,19 @@ def _add_license_diff_to_recipe(path, diff):
335def _create_new_recipe(newpv, checksums, srcrev, srcbranch, srcsubdir_old, srcsubdir_new, workspace, tinfoil, rd, license_diff, new_licenses, srctree, keep_failure): 336def _create_new_recipe(newpv, checksums, srcrev, srcbranch, srcsubdir_old, srcsubdir_new, workspace, tinfoil, rd, license_diff, new_licenses, srctree, keep_failure):
336 """Creates the new recipe under workspace""" 337 """Creates the new recipe under workspace"""
337 338
338 bpn = rd.getVar('BPN') 339 pn = rd.getVar('PN')
339 path = os.path.join(workspace, 'recipes', bpn) 340 path = os.path.join(workspace, 'recipes', pn)
340 bb.utils.mkdirhier(path) 341 bb.utils.mkdirhier(path)
341 copied, _ = oe.recipeutils.copy_recipe_files(rd, path, all_variants=True) 342 copied, _ = oe.recipeutils.copy_recipe_files(rd, path, all_variants=True)
342 if not copied: 343 if not copied:
343 raise DevtoolError('Internal error - no files were copied for recipe %s' % bpn) 344 raise DevtoolError('Internal error - no files were copied for recipe %s' % pn)
344 logger.debug('Copied %s to %s' % (copied, path)) 345 logger.debug('Copied %s to %s' % (copied, path))
345 346
346 oldpv = rd.getVar('PV') 347 oldpv = rd.getVar('PV')
347 if not newpv: 348 if not newpv:
348 newpv = oldpv 349 newpv = oldpv
349 origpath = rd.getVar('FILE') 350 origpath = rd.getVar('FILE')
350 fullpath = _rename_recipe_files(origpath, bpn, oldpv, newpv, path) 351 fullpath = _rename_recipe_files(origpath, pn, oldpv, newpv, path)
351 logger.debug('Upgraded %s => %s' % (origpath, fullpath)) 352 logger.debug('Upgraded %s => %s' % (origpath, fullpath))
352 353
353 newvalues = {} 354 newvalues = {}
@@ -534,6 +535,15 @@ def _generate_license_diff(old_licenses, new_licenses):
534 diff = diff + line 535 diff = diff + line
535 return diff 536 return diff
536 537
538def _run_recipe_upgrade_extra_tasks(pn, rd, tinfoil):
539 tasks = []
540 for task in (rd.getVar('RECIPE_UPGRADE_EXTRA_TASKS') or '').split():
541 logger.info('Running extra recipe upgrade task: %s' % task)
542 res = tinfoil.build_targets(pn, task, handle_events=True)
543
544 if not res:
545 raise DevtoolError('Running extra recipe upgrade task %s for %s failed' % (task, pn))
546
537def upgrade(args, config, basepath, workspace): 547def upgrade(args, config, basepath, workspace):
538 """Entry point for the devtool 'upgrade' subcommand""" 548 """Entry point for the devtool 'upgrade' subcommand"""
539 549
@@ -561,7 +571,7 @@ def upgrade(args, config, basepath, workspace):
561 else: 571 else:
562 srctree = standard.get_default_srctree(config, pn) 572 srctree = standard.get_default_srctree(config, pn)
563 573
564 srctree_s = standard.get_real_srctree(srctree, rd.getVar('S'), rd.getVar('WORKDIR')) 574 srctree_s = standard.get_real_srctree(srctree, rd.getVar('S'), rd.getVar('UNPACKDIR'))
565 575
566 # try to automatically discover latest version and revision if not provided on command line 576 # try to automatically discover latest version and revision if not provided on command line
567 if not args.version and not args.srcrev: 577 if not args.version and not args.srcrev:
@@ -601,7 +611,7 @@ def upgrade(args, config, basepath, workspace):
601 license_diff = _generate_license_diff(old_licenses, new_licenses) 611 license_diff = _generate_license_diff(old_licenses, new_licenses)
602 rf, copied = _create_new_recipe(args.version, checksums, args.srcrev, srcbranch, srcsubdir1, srcsubdir2, config.workspace_path, tinfoil, rd, license_diff, new_licenses, srctree, args.keep_failure) 612 rf, copied = _create_new_recipe(args.version, checksums, args.srcrev, srcbranch, srcsubdir1, srcsubdir2, config.workspace_path, tinfoil, rd, license_diff, new_licenses, srctree, args.keep_failure)
603 except (bb.process.CmdError, DevtoolError) as e: 613 except (bb.process.CmdError, DevtoolError) as e:
604 recipedir = os.path.join(config.workspace_path, 'recipes', rd.getVar('BPN')) 614 recipedir = os.path.join(config.workspace_path, 'recipes', rd.getVar('PN'))
605 _upgrade_error(e, recipedir, srctree, args.keep_failure) 615 _upgrade_error(e, recipedir, srctree, args.keep_failure)
606 standard._add_md5(config, pn, os.path.dirname(rf)) 616 standard._add_md5(config, pn, os.path.dirname(rf))
607 617
@@ -609,6 +619,8 @@ def upgrade(args, config, basepath, workspace):
609 copied, config.workspace_path, rd) 619 copied, config.workspace_path, rd)
610 standard._add_md5(config, pn, af) 620 standard._add_md5(config, pn, af)
611 621
622 _run_recipe_upgrade_extra_tasks(pn, rd, tinfoil)
623
612 update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn]) 624 update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn])
613 625
614 logger.info('Upgraded source extracted to %s' % srctree) 626 logger.info('Upgraded source extracted to %s' % srctree)
@@ -643,18 +655,28 @@ def latest_version(args, config, basepath, workspace):
643 return 0 655 return 0
644 656
645def check_upgrade_status(args, config, basepath, workspace): 657def check_upgrade_status(args, config, basepath, workspace):
658 def _print_status(recipe):
659 print("{:25} {:15} {:15} {} {} {}".format( recipe['pn'],
660 recipe['cur_ver'],
661 recipe['status'] if recipe['status'] != 'UPDATE' else (recipe['next_ver'] if not recipe['next_ver'].endswith("new-commits-available") else "new commits"),
662 recipe['maintainer'],
663 recipe['revision'] if recipe['revision'] != 'N/A' else "",
664 "cannot be updated due to: %s" %(recipe['no_upgrade_reason']) if recipe['no_upgrade_reason'] else ""))
646 if not args.recipe: 665 if not args.recipe:
647 logger.info("Checking the upstream status for all recipes may take a few minutes") 666 logger.info("Checking the upstream status for all recipes may take a few minutes")
648 results = oe.recipeutils.get_recipe_upgrade_status(args.recipe) 667 results = oe.recipeutils.get_recipe_upgrade_status(args.recipe)
649 for result in results: 668 for recipegroup in results:
650 # pn, update_status, current, latest, maintainer, latest_commit, no_update_reason 669 upgrades = [r for r in recipegroup if r['status'] != 'MATCH']
651 if args.all or result[1] != 'MATCH': 670 currents = [r for r in recipegroup if r['status'] == 'MATCH']
652 print("{:25} {:15} {:15} {} {} {}".format( result[0], 671 if len(upgrades) > 1:
653 result[2], 672 print("These recipes need to be upgraded together {")
654 result[1] if result[1] != 'UPDATE' else (result[3] if not result[3].endswith("new-commits-available") else "new commits"), 673 for r in sorted(upgrades, key=lambda r:r['pn']):
655 result[4], 674 _print_status(r)
656 result[5] if result[5] != 'N/A' else "", 675 if len(upgrades) > 1:
657 "cannot be updated due to: %s" %(result[6]) if result[6] else "")) 676 print("}")
677 for r in currents:
678 if args.all:
679 _print_status(r)
658 680
659def register_commands(subparsers, context): 681def register_commands(subparsers, context):
660 """Register devtool subcommands from this plugin""" 682 """Register devtool subcommands from this plugin"""
diff --git a/scripts/lib/devtool/utilcmds.py b/scripts/lib/devtool/utilcmds.py
index 964817766b..bf39f71b11 100644
--- a/scripts/lib/devtool/utilcmds.py
+++ b/scripts/lib/devtool/utilcmds.py
@@ -64,7 +64,7 @@ def configure_help(args, config, basepath, workspace):
64 b = rd.getVar('B') 64 b = rd.getVar('B')
65 s = rd.getVar('S') 65 s = rd.getVar('S')
66 configurescript = os.path.join(s, 'configure') 66 configurescript = os.path.join(s, 'configure')
67 confdisabled = 'noexec' in rd.getVarFlags('do_configure') or 'do_configure' not in (rd.getVar('__BBTASKS', False) or []) 67 confdisabled = 'noexec' in rd.getVarFlags('do_configure') or 'do_configure' not in (bb.build.listtasks(rd))
68 configureopts = oe.utils.squashspaces(rd.getVar('CONFIGUREOPTS') or '') 68 configureopts = oe.utils.squashspaces(rd.getVar('CONFIGUREOPTS') or '')
69 extra_oeconf = oe.utils.squashspaces(rd.getVar('EXTRA_OECONF') or '') 69 extra_oeconf = oe.utils.squashspaces(rd.getVar('EXTRA_OECONF') or '')
70 extra_oecmake = oe.utils.squashspaces(rd.getVar('EXTRA_OECMAKE') or '') 70 extra_oecmake = oe.utils.squashspaces(rd.getVar('EXTRA_OECMAKE') or '')