From 1edb401fe89b5e4c4ecc4d0cd160fba66b361ce1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 24 Feb 2026 19:32:40 +0100 Subject: [PATCH] ninjabackend: fix class file path to include build_subdir Compute the class file path relative to the private dir using the full name of the .class file, not just the src.fname. For example, for target subdir "java" and source "java/icd/Api.java", the class path is "icd/Api.class" rather than just "Api.class". Using the wrong class file name caused unnecessary rebuilds of jar files. Fixes: #15582 --- mesonbuild/backend/ninjabackend.py | 4 +++- .../com-mesonbuild-HelloWorldMainLauncherClass.java | 7 +++++++ test cases/java/11 build subdir/copyfile.py | 6 ++++++ test cases/java/11 build subdir/meson.build | 13 +++++++++++++ unittests/platformagnostictests.py | 10 +++++++++- 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 test cases/java/11 build subdir/com-mesonbuild-HelloWorldMainLauncherClass.java create mode 100644 test cases/java/11 build subdir/copyfile.py create mode 100644 test cases/java/11 build subdir/meson.build diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 353984b14..3a1ab3f07 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1614,7 +1614,9 @@ class NinjaBackend(backends.Backend): rel_src = src.rel_to_builddir(self.build_to_src) rel_srcs.append(rel_src) - plain_class_path = src.fname[:-4] + 'class' + # Preserve any additional path components on top of the target's subdir + plain_class_path = os.path.relpath(src.relative_name(), target.get_subdir()) + plain_class_path = plain_class_path[:-4] + 'class' plain_class_paths.append(plain_class_path) rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path) rel_objs.append(rel_obj) diff --git a/test cases/java/11 build subdir/com-mesonbuild-HelloWorldMainLauncherClass.java b/test cases/java/11 build subdir/com-mesonbuild-HelloWorldMainLauncherClass.java new file mode 100644 index 000000000..0e81bb7d5 --- /dev/null +++ b/test cases/java/11 build subdir/com-mesonbuild-HelloWorldMainLauncherClass.java @@ -0,0 +1,7 @@ +package com.mesonbuild; + +public class HelloWorldMainLauncherClass { + public static void main(String [] args) { + System.out.println("hello nanowar of steel"); + } +} diff --git a/test cases/java/11 build subdir/copyfile.py b/test cases/java/11 build subdir/copyfile.py new file mode 100644 index 000000000..ff42ac359 --- /dev/null +++ b/test cases/java/11 build subdir/copyfile.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import sys +import shutil + +shutil.copyfile(sys.argv[1], sys.argv[2]) diff --git a/test cases/java/11 build subdir/meson.build b/test cases/java/11 build subdir/meson.build new file mode 100644 index 000000000..762d037f6 --- /dev/null +++ b/test cases/java/11 build subdir/meson.build @@ -0,0 +1,13 @@ +project('build subdir java', 'java') + +copyfile = find_program('copyfile.py') + +generated = custom_target( + command: [copyfile, '@INPUT@', '@OUTPUT@'], + input: 'com-mesonbuild-HelloWorldMainLauncherClass.java', + output: 'HelloWorldMainLauncherClass.java', + build_subdir: 'com/mesonbuild', +) + +jar('hello', generated, + main_class: 'com.mesonbuild.HelloWorldMainLauncherClass') diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index f6cd77952..c825907c2 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -15,7 +15,7 @@ from unittest import skipIf, SkipTest from pathlib import Path from .baseplatformtests import BasePlatformTests -from .helpers import IS_CI +from .helpers import * from mesonbuild.mesonlib import EnvironmentVariables, ExecutableSerialisation, MesonException, is_linux, python_command, windows_proof_rmtree from mesonbuild.mformat import Formatter, match_path from mesonbuild.optinterpreter import OptionInterpreter, OptionException @@ -626,3 +626,11 @@ class PlatformAgnosticTests(BasePlatformTests): self.init(testdir) self.build() + + @skip_if_not_language('java') + def test_java_build_subdir_correct_deps(self): + """Test that jar() with build_subdir sources does not rebuild unnecessarily.""" + testdir = os.path.join(self.src_root, 'test cases', 'java', '11 build subdir') + self.init(testdir) + self.build() + self.assertBuildIsNoop()