The run_merge_config() function constructs paths for merge_config.sh using out_dir and cfg_file which are relative to the original working directory. However, the commands run with cwd=src_dir (the work directory), so these paths resolve incorrectly. For example, with src_dir='../exph/.bm-work/00' and out_dir='../exph/.bm-work/00/build', the -O flag would pass the full out_dir path. When make runs from src_dir, it interprets this as '../exph/.bm-work/00/../exph/.bm-work/00/build', doubling the path. Fix this by converting out_dir and cfg_file to paths relative to src_dir using os.path.relpath(). This ensures the paths resolve correctly when commands execute from the work directory. Fixes: 635c5f5638a0 ("buildman: Use merge_config.sh for --adjust-cfg") Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- tools/buildman/cfgutil.py | 21 ++++++++++----- tools/buildman/test_cfgutil.py | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/tools/buildman/cfgutil.py b/tools/buildman/cfgutil.py index cec33e1e62b..060f2762b96 100644 --- a/tools/buildman/cfgutil.py +++ b/tools/buildman/cfgutil.py @@ -368,9 +368,18 @@ def run_merge_config(src_dir, out_dir, cfg_file, adjust_cfg, env): # Create a minimal defconfig from the current .config # This is necessary for 'imply' to work - the full .config has # '# CONFIG_xxx is not set' lines that prevent imply from taking effect - defconfig_path = os.path.join(out_dir or '.', 'defconfig') - make_cmd = ['make', f'O={out_dir}' if out_dir else None, - f'KCONFIG_CONFIG={cfg_file}', 'savedefconfig'] + # + # Convert paths to be relative to src_dir since commands run with + # cwd=src_dir + if src_dir and out_dir: + rel_out_dir = os.path.relpath(out_dir, src_dir) + rel_cfg_file = os.path.relpath(cfg_file, src_dir) + else: + rel_out_dir = out_dir or '.' + rel_cfg_file = cfg_file + defconfig_path = os.path.join(rel_out_dir, 'defconfig') + make_cmd = ['make', f'O={rel_out_dir}' if rel_out_dir != '.' else None, + f'KCONFIG_CONFIG={rel_cfg_file}', 'savedefconfig'] make_cmd = [x for x in make_cmd if x] # Remove None elements result = command.run_one(*make_cmd, cwd=src_dir, env=env, capture=True, capture_stderr=True) @@ -382,10 +391,8 @@ def run_merge_config(src_dir, out_dir, cfg_file, adjust_cfg, env): try: # Run merge_config.sh with the minimal defconfig as base # -O sets output dir; defconfig is the base, fragment is merged - merge_script = os.path.join(src_dir or '.', 'scripts', 'kconfig', - 'merge_config.sh') - out = out_dir or '.' - cmd = [merge_script, '-O', out, defconfig_path, frag_path] + merge_script = os.path.join('scripts', 'kconfig', 'merge_config.sh') + cmd = [merge_script, '-O', rel_out_dir, defconfig_path, frag_path] result = command.run_one(*cmd, cwd=src_dir, env=env, capture=True, capture_stderr=True) finally: diff --git a/tools/buildman/test_cfgutil.py b/tools/buildman/test_cfgutil.py index 47e522d3d6c..b623a4c4f67 100644 --- a/tools/buildman/test_cfgutil.py +++ b/tools/buildman/test_cfgutil.py @@ -180,6 +180,54 @@ class TestAdjustCfg(unittest.TestCase): result) +class TestRunMergeConfig(unittest.TestCase): + """Tests for run_merge_config() function""" + + def test_merge_script_path(self): + """Test that merge_config.sh path is relative to cwd, not absolute""" + from unittest import mock + from u_boot_pylib import command + + # Track commands that were run + commands_run = [] + + def mock_run_one(*args, **kwargs): + commands_run.append((args, kwargs)) + result = command.CommandResult() + result.return_code = 0 + result.stdout = '' + result.stderr = '' + return result + + with mock.patch.object(command, 'run_one', mock_run_one): + with mock.patch('os.path.exists', return_value=True): + with mock.patch('os.unlink'): + # Use a work directory path like buildman does + src_dir = '../branch/.bm-work/00' + cfgutil.run_merge_config( + src_dir, 'build', 'build/.config', + {'LOCALVERSION_AUTO': '~LOCALVERSION_AUTO'}, {}) + + # Find the merge_config.sh command + merge_cmd = None + for args, kwargs in commands_run: + if args and 'merge_config.sh' in args[0]: + merge_cmd = args + merge_cwd = kwargs.get('cwd') + break + + self.assertIsNotNone(merge_cmd, 'merge_config.sh command not found') + + # The script path should be relative, not include src_dir + script_path = merge_cmd[0] + self.assertEqual('scripts/kconfig/merge_config.sh', script_path, + f'Script path should be relative, got: {script_path}') + + # The cwd should be src_dir + self.assertEqual(src_dir, merge_cwd, + f'cwd should be src_dir, got: {merge_cwd}') + + class TestProcessConfig(unittest.TestCase): """Tests for process_config() function""" -- 2.43.0