diff mbox

[U-Boot,v2,2/2] tools: moveconfig: Add a new --git-ref option

Message ID 1464838207-10020-2-git-send-email-joe.hershberger@ni.com
State Changes Requested
Delegated to: Masahiro Yamada
Headers show

Commit Message

Joe Hershberger June 2, 2016, 3:30 a.m. UTC
This option allows the 'make *_defconfig' step to run against a former
repo state, while the savedefconfig step runs against the current repo
state. This is convenient for the case where something in the Kconfig
has changed such that the defconfig is no longer complete with the new
Kconfigs. This feature allows the .config to be built assuming those old
Kconfigs, but then savedefconfig based on the new state of the Kconfigs.

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
---

Changes in v2:
- Stop reusing functions from patman
- Rebase on top of the latest moveconfig series

 tools/moveconfig.py | 101 +++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 84 insertions(+), 17 deletions(-)

Comments

Masahiro Yamada June 8, 2016, 2:56 a.m. UTC | #1
Hi Joe,


2016-06-02 12:30 GMT+09:00 Joe Hershberger <joe.hershberger@ni.com>:
> This option allows the 'make *_defconfig' step to run against a former
> repo state, while the savedefconfig step runs against the current repo
> state. This is convenient for the case where something in the Kconfig
> has changed such that the defconfig is no longer complete with the new
> Kconfigs. This feature allows the .config to be built assuming those old
> Kconfigs, but then savedefconfig based on the new state of the Kconfigs.
>
> Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>


Basically, your idea seems cool,
but please improve the implementation.

It looks like your patch includes various noises (more changes than needed),
and things are getting complex.





> @@ -412,6 +416,9 @@ class KconfigParser:
>          self.options = options
>          self.dotconfig = os.path.join(build_dir, '.config')
>          self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
> +        if options.git_ref:
> +            self.autoconf_orig = os.path.join(build_dir, 'include',
> +                                              'autoconf.orig.mk')


This change is not needed.  (see below)



>          self.config_autoconf = os.path.join(build_dir, 'include', 'config',
>                                              'auto.conf')
>          self.defconfig = os.path.join(build_dir, 'defconfig')
> @@ -464,14 +471,6 @@ class KconfigParser:
>          """
>          not_set = '# %s is not set' % config
>
> -        for line in dotconfig_lines:
> -            line = line.rstrip()
> -            if line.startswith(config + '=') or line == not_set:
> -                old_val = line
> -                break
> -        else:
> -            return (ACTION_NO_ENTRY, config)
> -
>          for line in autoconf_lines:
>              line = line.rstrip()
>              if line.startswith(config + '='):
> @@ -480,6 +479,14 @@ class KconfigParser:
>          else:
>              new_val = not_set
>
> +        for line in dotconfig_lines:
> +            line = line.rstrip()
> +            if line.startswith(config + '=') or line == not_set:
> +                old_val = line
> +                break
> +        else:
> +            return (ACTION_NO_ENTRY, config)
> +

Why did you move this hunk?




>          if old_val == new_val:
>              return (ACTION_NO_CHANGE, new_val)
>
> @@ -515,8 +522,14 @@ class KconfigParser:
>          with open(self.dotconfig) as f:
>              dotconfig_lines = f.readlines()
>
> -        with open(self.autoconf) as f:
> -            autoconf_lines = f.readlines()
> +
> +        if self.options.git_ref:
> +            # Pull the target value from the original autoconf.mk
> +            with open(self.autoconf_orig) as f:
> +                autoconf_lines = f.readlines()
> +        else:
> +            with open(self.autoconf) as f:
> +                autoconf_lines = f.readlines()

Unneeded.  (see below)


>          for config in self.configs:
>              result = self.parse_one_config(config, dotconfig_lines,
> @@ -585,7 +598,7 @@ class Slot:
>      for faster processing.
>      """
>
> -    def __init__(self, configs, options, progress, devnull, make_cmd):
> +    def __init__(self, configs, options, progress, devnull, make_cmd, defconfig_src_dir):
>          """Create a new process slot.
>
>          Arguments:

In this v2 approach, defconfig occurs twice;
first against the cwd tree, 2nd against the cloned-tree.
So, "defconfig_src_dir" does not best-describe the behavior, I think.
Could you rename "defconfig_src_dir" to something more suitable?
Maybe, "ref_src_dir" or something?


Also, when you add a new argument,
please add a comment to "Arguments:"



> @@ -600,8 +613,11 @@ class Slot:
>          self.build_dir = tempfile.mkdtemp()
>          self.devnull = devnull
>          self.make_cmd = (make_cmd, 'O=' + self.build_dir)
> +        self.defconfig_src_dir = defconfig_src_dir

Please consider renaming it.

>          self.parser = KconfigParser(configs, options, self.build_dir)
>          self.state = STATE_IDLE
> +        if self.options.git_ref:
> +            self.use_git_ref = True

This is not needed because it is always
initialized in the add() method.


>          self.failed_boards = []
>
>      def __del__(self):
> @@ -609,7 +625,7 @@ class Slot:
>
>          This function makes sure the temporary directory is cleaned away
>          even if Python suddenly dies due to error.  It should be done in here
> -        because it is guranteed the destructor is always invoked when the
> +        because it is guaranteed the destructor is always invoked when the
>          instance of the class gets unreferenced.
>
>          If the subprocess is still running, wait until it finishes.
> @@ -638,6 +654,7 @@ class Slot:
>          self.defconfig = defconfig
>          self.state = STATE_INIT
>          self.log = ''
> +        self.use_git_ref = True

Setting always use_git_ref to True seems odd.

Maybe, can you change it like this?

    self.use_git_ref = True if self.options.git_ref else False



>          return True
>
>      def poll(self):
> @@ -662,6 +679,9 @@ class Slot:
>          if self.state == STATE_INIT:
>              cmd = list(self.make_cmd)
>              cmd.append(self.defconfig)
> +            if self.options.git_ref and self.use_git_ref:

With my suggestion above, checking self.use_git_ref should be enough.

               if self.use_git_ref:


> +                cmd.append('-C')
> +                cmd.append(self.defconfig_src_dir)
>              self.ps = subprocess.Popen(cmd, stdout=self.devnull,
>                                         stderr=subprocess.PIPE)
>              self.state = STATE_DEFCONFIG
> @@ -692,12 +712,22 @@ class Slot:
>                  cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
>              cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
>              cmd.append('include/config/auto.conf')
> +            if self.options.git_ref and self.use_git_ref:


Ditto.

                if self.use_git_ref:




> +                cmd.append('-C')
> +                cmd.append(self.defconfig_src_dir)
>              self.ps = subprocess.Popen(cmd, stdout=self.devnull,
>                                         stderr=subprocess.PIPE)
>              self.state = STATE_AUTOCONF
>              return False
>
>          if self.state == STATE_AUTOCONF:
> +            if self.options.git_ref and self.use_git_ref:
> +                shutil.move(os.path.join(self.build_dir, 'include/autoconf.mk'),
> +                            os.path.join(self.build_dir, 'include/autoconf.orig.mk'))
> +                self.state = STATE_INIT
> +                self.use_git_ref = False
> +                return False
> +
>              (updated, log) = self.parser.update_dotconfig()
>              self.log += log

As far as I understood, if -r options is specified,
the state moves like this.

(1) STATE_IDLE
(2) STATE_INIT
(3) STATE_DEFCONFIG
(4) STATE_AUTOCONF
(5) STATE_INIT (2nd)
(6) STATE_DEFCONFIG (2nd)
(7) STATE_AUTOCONF (2nd)
(8) STATE_SAVEDEFCONFIG


But, is the 2nd autoconf necessary?
We only want to use autoconf.mk from the first autoconf.
The second one is just harmful; it overwrites autoconf.mk we want to parse.

If the 2nd one is skipped, we do not need to copy
it to include/autoconf.orig.mk

I understand why you did so.
The state transition is getting very complicated.

After pondering on it for a while,
I decided splitting code into helper methods might get the
situation better.

Please see my this follow-up patch.
http://patchwork.ozlabs.org/patch/631921/


With the patch applied, the poll() method is like this,


        if self.ps.poll() != 0:
            self.handle_error()
        elif self.state == STATE_DEFCONFIG:
            self.do_autoconf()
        elif self.state == STATE_AUTOCONF:
            self.do_savedefconfig()
        elif self.state == STATE_SAVEDEFCONFIG:
            self.update_defconfig()
        else:
            sys.exit("Internal Error. This should not happen.")




-r option might be implemented like this:


        if self.ps.poll() != 0:
            self.handle_error()
        elif self.state == STATE_DEFCONFIG:
            if self.options.git_ref and not self.use_git_ref:
                self.do_savedefconfig()
            else:
                self.do_autoconf()
        elif self.state == STATE_AUTOCONF:
            if self.use_git_ref:
                self.use_git_ref = False
                self.do_defconfig
            else:
                self.do_savedefconfig()
        elif self.state == STATE_SAVEDEFCONFIG:
            self.update_defconfig()
        else:
            sys.exit("Internal Error. This should not happen.")






> @@ -724,7 +754,7 @@ class Slot:
>              updated = not filecmp.cmp(orig_defconfig, new_defconfig)
>
>              if updated:
> -                self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
> +                self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
>                                         "defconfig was updated.\n")

Unrelated change.

You should send a separate patch if you want to change it.




> @@ -853,11 +901,28 @@ def move_config(configs, options):
>          if options.force_sync:
>              print 'No CONFIG is specified. You are probably syncing defconfigs.',
>          else:
> -            print 'Neigher CONFIG nor --force-sync is specified. Nothing will happen.',
> +            print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
>      else:
>          print 'Move ' + ', '.join(configs),
>      print '(jobs: %d)\n' % options.jobs

I will fix this typo in my patch.

Please drop this hunk when you send a new patch.



> +    defconfig_src_dir = ''
> +
> +    if options.git_ref:
> +        work_dir = WorkDir()
> +        defconfig_src_dir = work_dir.get()
> +        cwd = os.getcwd()
> +        print "Cloning git repo for 'make *_defconfig' step..."
> +       subprocess.check_output(['git', 'clone', cwd, '.'], \
> +                               cwd=defconfig_src_dir)
> +        print "Checkout '%s' to find original configs." % \
> +            subprocess.check_output(['git', 'rev-parse', '--short', \
> +                                   options.git_ref]).strip()
> +        os.chdir(defconfig_src_dir)
> +       subprocess.check_output(['git', 'checkout', options.git_ref],
> +                                stderr=subprocess.STDOUT)
> +        os.chdir(cwd)
> +

Please use cmd= option instead of os.chdir()

  subprocess.check_output(['git', 'checkout', options.git_ref],
                           stderr=subprocess.STDOUT,
                           cwd=defconfig_src_dir)






To sum up,

Apply my series without 11/21,
then apply  http://patchwork.ozlabs.org/patch/631921/,
then could you consider rebasing your 2/2 on top of that?


Thanks,
diff mbox

Patch

diff --git a/tools/moveconfig.py b/tools/moveconfig.py
index 01350ce..7d9d31e 100755
--- a/tools/moveconfig.py
+++ b/tools/moveconfig.py
@@ -143,6 +143,10 @@  Available options
    Specify the number of threads to run simultaneously.  If not specified,
    the number of threads is the same as the number of CPU cores.
 
+ -r, --git-ref
+   Specify the git ref to clone for the make *_defconfig step. If unspecified
+   use the CWD.
+
  -v, --verbose
    Show any build errors as boards are built
 
@@ -412,6 +416,9 @@  class KconfigParser:
         self.options = options
         self.dotconfig = os.path.join(build_dir, '.config')
         self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
+        if options.git_ref:
+            self.autoconf_orig = os.path.join(build_dir, 'include',
+                                              'autoconf.orig.mk')
         self.config_autoconf = os.path.join(build_dir, 'include', 'config',
                                             'auto.conf')
         self.defconfig = os.path.join(build_dir, 'defconfig')
@@ -464,14 +471,6 @@  class KconfigParser:
         """
         not_set = '# %s is not set' % config
 
-        for line in dotconfig_lines:
-            line = line.rstrip()
-            if line.startswith(config + '=') or line == not_set:
-                old_val = line
-                break
-        else:
-            return (ACTION_NO_ENTRY, config)
-
         for line in autoconf_lines:
             line = line.rstrip()
             if line.startswith(config + '='):
@@ -480,6 +479,14 @@  class KconfigParser:
         else:
             new_val = not_set
 
+        for line in dotconfig_lines:
+            line = line.rstrip()
+            if line.startswith(config + '=') or line == not_set:
+                old_val = line
+                break
+        else:
+            return (ACTION_NO_ENTRY, config)
+
         if old_val == new_val:
             return (ACTION_NO_CHANGE, new_val)
 
@@ -515,8 +522,14 @@  class KconfigParser:
         with open(self.dotconfig) as f:
             dotconfig_lines = f.readlines()
 
-        with open(self.autoconf) as f:
-            autoconf_lines = f.readlines()
+
+        if self.options.git_ref:
+            # Pull the target value from the original autoconf.mk
+            with open(self.autoconf_orig) as f:
+                autoconf_lines = f.readlines()
+        else:
+            with open(self.autoconf) as f:
+                autoconf_lines = f.readlines()
 
         for config in self.configs:
             result = self.parse_one_config(config, dotconfig_lines,
@@ -585,7 +598,7 @@  class Slot:
     for faster processing.
     """
 
-    def __init__(self, configs, options, progress, devnull, make_cmd):
+    def __init__(self, configs, options, progress, devnull, make_cmd, defconfig_src_dir):
         """Create a new process slot.
 
         Arguments:
@@ -600,8 +613,11 @@  class Slot:
         self.build_dir = tempfile.mkdtemp()
         self.devnull = devnull
         self.make_cmd = (make_cmd, 'O=' + self.build_dir)
+        self.defconfig_src_dir = defconfig_src_dir
         self.parser = KconfigParser(configs, options, self.build_dir)
         self.state = STATE_IDLE
+        if self.options.git_ref:
+            self.use_git_ref = True
         self.failed_boards = []
 
     def __del__(self):
@@ -609,7 +625,7 @@  class Slot:
 
         This function makes sure the temporary directory is cleaned away
         even if Python suddenly dies due to error.  It should be done in here
-        because it is guranteed the destructor is always invoked when the
+        because it is guaranteed the destructor is always invoked when the
         instance of the class gets unreferenced.
 
         If the subprocess is still running, wait until it finishes.
@@ -638,6 +654,7 @@  class Slot:
         self.defconfig = defconfig
         self.state = STATE_INIT
         self.log = ''
+        self.use_git_ref = True
         return True
 
     def poll(self):
@@ -662,6 +679,9 @@  class Slot:
         if self.state == STATE_INIT:
             cmd = list(self.make_cmd)
             cmd.append(self.defconfig)
+            if self.options.git_ref and self.use_git_ref:
+                cmd.append('-C')
+                cmd.append(self.defconfig_src_dir)
             self.ps = subprocess.Popen(cmd, stdout=self.devnull,
                                        stderr=subprocess.PIPE)
             self.state = STATE_DEFCONFIG
@@ -692,12 +712,22 @@  class Slot:
                 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
             cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
             cmd.append('include/config/auto.conf')
+            if self.options.git_ref and self.use_git_ref:
+                cmd.append('-C')
+                cmd.append(self.defconfig_src_dir)
             self.ps = subprocess.Popen(cmd, stdout=self.devnull,
                                        stderr=subprocess.PIPE)
             self.state = STATE_AUTOCONF
             return False
 
         if self.state == STATE_AUTOCONF:
+            if self.options.git_ref and self.use_git_ref:
+                shutil.move(os.path.join(self.build_dir, 'include/autoconf.mk'),
+                            os.path.join(self.build_dir, 'include/autoconf.orig.mk'))
+                self.state = STATE_INIT
+                self.use_git_ref = False
+                return False
+
             (updated, log) = self.parser.update_dotconfig()
             self.log += log
 
@@ -724,7 +754,7 @@  class Slot:
             updated = not filecmp.cmp(orig_defconfig, new_defconfig)
 
             if updated:
-                self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
+                self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
                                        "defconfig was updated.\n")
 
             if not self.options.dry_run and updated:
@@ -771,7 +801,7 @@  class Slots:
 
     """Controller of the array of subprocess slots."""
 
-    def __init__(self, configs, options, progress):
+    def __init__(self, configs, options, progress, defconfig_src_dir):
         """Create a new slots controller.
 
         Arguments:
@@ -785,7 +815,7 @@  class Slots:
         make_cmd = get_make_cmd()
         for i in range(options.jobs):
             self.slots.append(Slot(configs, options, progress, devnull,
-                                   make_cmd))
+                                   make_cmd, defconfig_src_dir))
 
     def add(self, defconfig):
         """Add a new subprocess if a vacant slot is found.
@@ -842,6 +872,24 @@  class Slots:
                 for board in failed_boards:
                     f.write(board + '\n')
 
+class WorkDir:
+    def __init__(self):
+        """Create a new working directory."""
+        self.work_dir = tempfile.mkdtemp()
+
+    def __del__(self):
+        """Delete the working directory
+
+        This function makes sure the temporary directory is cleaned away
+        even if Python suddenly dies due to error.  It should be done in here
+        because it is guaranteed the destructor is always invoked when the
+        instance of the class gets unreferenced.
+        """
+        shutil.rmtree(self.work_dir)
+
+    def get(self):
+        return self.work_dir
+
 def move_config(configs, options):
     """Move config options to defconfig files.
 
@@ -853,11 +901,28 @@  def move_config(configs, options):
         if options.force_sync:
             print 'No CONFIG is specified. You are probably syncing defconfigs.',
         else:
-            print 'Neigher CONFIG nor --force-sync is specified. Nothing will happen.',
+            print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
     else:
         print 'Move ' + ', '.join(configs),
     print '(jobs: %d)\n' % options.jobs
 
+    defconfig_src_dir = ''
+
+    if options.git_ref:
+        work_dir = WorkDir()
+        defconfig_src_dir = work_dir.get()
+        cwd = os.getcwd()
+        print "Cloning git repo for 'make *_defconfig' step..."
+	subprocess.check_output(['git', 'clone', cwd, '.'], \
+				cwd=defconfig_src_dir)
+        print "Checkout '%s' to find original configs." % \
+            subprocess.check_output(['git', 'rev-parse', '--short', \
+				    options.git_ref]).strip()
+        os.chdir(defconfig_src_dir)
+	subprocess.check_output(['git', 'checkout', options.git_ref],
+                                stderr=subprocess.STDOUT)
+        os.chdir(cwd)
+
     if options.defconfigs:
         defconfigs = [line.strip() for line in open(options.defconfigs)]
         for i, defconfig in enumerate(defconfigs):
@@ -875,7 +940,7 @@  def move_config(configs, options):
                 defconfigs.append(os.path.join(dirpath, filename))
 
     progress = Progress(len(defconfigs))
-    slots = Slots(configs, options, progress)
+    slots = Slots(configs, options, progress, defconfig_src_dir)
 
     # Main loop to process defconfig files:
     #  Add a new subprocess into a vacant slot.
@@ -917,6 +982,8 @@  def main():
                       help='only cleanup the headers')
     parser.add_option('-j', '--jobs', type='int', default=cpu_count,
                       help='the number of jobs to run simultaneously')
+    parser.add_option('-r', '--git-ref', type='string',
+                      help='the git ref to clone for the make *_defconfig step')
     parser.add_option('-v', '--verbose', action='store_true', default=False,
                       help='show any build errors as boards are built')
     parser.usage += ' CONFIG ...'