depfixer: silence fix_jar() and make it do something

fix_jar() tries to remove an existing Class-Path entry from the jar
manifest by postprocessing the manifest and passing it to `jar -um`.
However, `jar -um` can only add/replace manifest entries, not remove
them, and it also complains loudly when replacing an entry:

    Dec 13, 2022 7:11:19 PM java.util.jar.Attributes read
    WARNING: Duplicate name in Manifest: Manifest-Version.
    Ensure that the manifest does not have duplicate entries, and
    that blank lines separate individual sections in both your
    manifest and in the META-INF/MANIFEST.MF entry in the jar file.

Thus fix_jar() produces one such warning for each entry in the manifest
and accomplishes nothing else.

Use jar -uM instead.  This completely removes the manifest from the jar
and allows adding it back as a normal zip member, fixing fix_jar() and
avoiding the warnings.

Fixes: https://github.com/mesonbuild/meson/issues/10491
Fixes: c70a051e93 ("java: remove manifest classpath from installed jar")
pull/11174/head
Benjamin Gilbert 2 years ago
parent 51c889ddbc
commit 35e230e48c
  1. 7
      mesonbuild/scripts/depfixer.py
  2. 7
      test cases/unit/110 classpath/com/mesonbuild/Simple.java
  3. 14
      test cases/unit/110 classpath/meson.build
  4. 17
      unittests/helpers.py
  5. 18
      unittests/machinefiletests.py

@ -465,7 +465,12 @@ def fix_jar(fname: str) -> None:
if not line.startswith('Class-Path:'):
f.write(line)
f.truncate()
subprocess.check_call(['jar', 'ufm', fname, 'META-INF/MANIFEST.MF'])
# jar -um doesn't allow removing existing attributes. Use -uM instead,
# which a) removes the existing manifest from the jar and b) disables
# special-casing for the manifest file, so we can re-add it as a normal
# archive member. This puts the manifest at the end of the jar rather
# than the beginning, but the spec doesn't forbid that.
subprocess.check_call(['jar', 'ufM', fname, 'META-INF/MANIFEST.MF'])
def fix_rpath(fname: str, rpath_dirs_to_remove: T.Set[bytes], new_rpath: T.Union[str, bytes], final_path: str, install_name_mappings: T.Dict[str, str], verbose: bool = True) -> None:
global INSTALL_NAME_TOOL # pylint: disable=global-statement

@ -0,0 +1,7 @@
package com.mesonbuild;
class Simple {
public static void main(String [] args) {
System.out.println("Java is working.\n");
}
}

@ -0,0 +1,14 @@
project('simplejava', 'java')
one = jar('one', 'com/mesonbuild/Simple.java',
main_class : 'com.mesonbuild.Simple',
install : true,
install_dir : get_option('bindir'),
)
two = jar('two', 'com/mesonbuild/Simple.java',
main_class : 'com.mesonbuild.Simple',
install : true,
install_dir : get_option('bindir'),
link_with : one,
)

@ -5,6 +5,7 @@ import unittest
import functools
import re
import typing as T
import zipfile
from pathlib import Path
from contextlib import contextmanager
@ -176,6 +177,22 @@ def get_rpath(fname: str) -> T.Optional[str]:
return None
return final
def get_classpath(fname: str) -> T.Optional[str]:
with zipfile.ZipFile(fname) as zip:
with zip.open('META-INF/MANIFEST.MF') as member:
contents = member.read().decode().strip()
lines = []
for line in contents.splitlines():
if line.startswith(' '):
# continuation line
lines[-1] += line[1:]
else:
lines.append(line)
manifest = {
k.lower(): v.strip() for k, v in [l.split(':', 1) for l in lines]
}
return manifest.get('class-path')
def get_path_without_cmd(cmd: str, path: str) -> str:
pathsep = os.pathsep
paths = OrderedSet([Path(p).resolve() for p in path.split(pathsep)])

@ -42,6 +42,7 @@ import mesonbuild.modules.pkgconfig
from run_tests import (
Backend,
get_fake_env
)
@ -368,6 +369,23 @@ class NativeFileTests(BasePlatformTests):
self._single_implementation_compiler(
'java', 'javac', 'javac 9.99.77', '9.99.77')
@skip_if_not_language('java')
def test_java_classpath(self):
if self.backend is not Backend.ninja:
raise SkipTest('Jar is only supported with Ninja')
testdir = os.path.join(self.unit_test_dir, '110 classpath')
self.init(testdir)
self.build()
one_build_path = get_classpath(os.path.join(self.builddir, 'one.jar'))
self.assertIsNone(one_build_path)
two_build_path = get_classpath(os.path.join(self.builddir, 'two.jar'))
self.assertEqual(two_build_path, 'one.jar')
self.install()
one_install_path = get_classpath(os.path.join(self.installdir, 'usr/bin/one.jar'))
self.assertIsNone(one_install_path)
two_install_path = get_classpath(os.path.join(self.installdir, 'usr/bin/two.jar'))
self.assertIsNone(two_install_path)
@skip_if_not_language('swift')
def test_swift_compiler(self):
wrapper = self.helper_create_binary_wrapper(

Loading…
Cancel
Save