bnd icon indicating copy to clipboard operation
bnd copied to clipboard

Unable to use includeresources with pattern on path

Open reckart opened this issue 4 months ago • 8 comments

I would like to create a JAR file using bnd that contains no classes and only some explicitly defined resources that reside in certain paths using the bnd-maven-plugin.

I have been able to get it work for simple filename based patterns like (https://github.com/bndtools/bnd/issues/6794#issuecomment-3250170088)

        <configuration>
          <includeClassesDir>false</includeClassesDir>
          <bnd>
          -includeresource: /=src/main/resources/;filter:="*.json"
          </bnd>
        </configuration>

But it does not seem to work for matching paths, e.g.

          -includeresource: /=src/main/resources/;filter:="**/*.json"

          -includeresource: /=src/main/resources/;filter:="*/data/*"

          -includeresource: /=src/main/resources/;filter:="**/data/**"

If I read the source correctly, then it is not possible to use includeresources with a pattern to match a path:

https://github.com/bndtools/bnd/blob/master/biz.aQute.bndlib/src/aQute/bnd/osgi/Instruction.java#L33-L48

reckart avatar Sep 04 '25 06:09 reckart

If I read the source correctly, then it is not possible to use includeresources with a pattern to match a path:

https://github.com/bndtools/bnd/blob/master/biz.aQute.bndlib/src/aQute/bnd/osgi/Instruction.java#L33-L48

I think you are right @reckart Also the docs state it: https://bnd.bndtools.org/instructions/includeresource.html

The filter: directive is an optional filter on the resources. This uses the same format as the instructions. Only the file name is verified against this instruction.

What do you propose how to go about this? I think we need to keep the current sematics of :filter directive as is to stay backwards compatible. Do you think it should be a new directive which specifies the behavior of :filter?

For example :filtermode:

  • :filtermode=name (current default)
  • :filtermode=path (the new one which goes to the full path

Or would you instead use a completely new directive like e.g. :pathfilter which would consider the whole path?

chrisrueger avatar Sep 16 '25 18:09 chrisrueger

Considering that dir separators are not valid in regular filenames, I don't think that there it would be a breaking change to perform a proper path matching if the filter expression contains a dir separator (in particular /)?

reckart avatar Sep 16 '25 19:09 reckart

So you mean something like this (pseudo code)?

if (instruction.contains("/")){
 // do pathmatching
}
else{
 // do what we do right now in https://github.com/bndtools/bnd/blob/master/biz.aQute.bndlib/src/aQute/bnd/osgi/Instruction.java#L33-L48
}

chrisrueger avatar Sep 16 '25 19:09 chrisrueger

Right. Maybe I am missing something though...

If we currently have something like filter:=foo.txt, it actually means filter:=**/foo.txt in path semantics. That means if a filter does not contain a /, it would mean prepending **/ and applying path matching semantics.

If one wanted to express just foo.txt in the top-level folder (currently impossible), it would mean something like /foo.txt or maybe ./foo.txt - which should also be compatible with path-matching semantics.

Do you see any potentially problematic case?

reckart avatar Sep 16 '25 19:09 reckart

Do you see any potentially problematic case?

I am not completely sure, we would need to try it out I guess.

I am not sure if the current matcher in Instruction.java is really doing ant-style path matching. But I currently assume we would not change the matching behavior of Instruction.java but just use the path instead of the name only, right?

Here is a quick ChatGPTed base for discussion, just to see if we are on the same page.

public static class Filter implements FileFilter {
    private final static Pattern DEFAULT_DO_NOT_COPY_P = Pattern.compile(Constants.DEFAULT_DO_NOT_COPY);
    private final Instruction instruction;
    private final boolean recursive;
    private final Pattern doNotCopy;
    private final boolean pathMatching;

    public Filter(Instruction instruction, boolean recursive, Pattern doNotCopy) {
        this.instruction = instruction;
        this.recursive = recursive;
        this.doNotCopy = doNotCopy;
        // pathMatching is true if instruction contains a "/" in its original string form
        this.pathMatching = instruction != null && instruction.toString().contains("/");
    }

    public Filter(Instruction instruction, boolean recursive) {
        this(instruction, recursive, DEFAULT_DO_NOT_COPY_P);
    }

    public boolean isRecursive() {
        return recursive;
    }

    @Override
    public boolean accept(File pathname) {
        String name = pathname.getName();

        if (pathMatching) {
            // --- New logic: path-based matching ---
            String fullPath = pathname.getPath().replace(File.separatorChar, '/');
            if (doNotCopy != null && doNotCopy.matcher(fullPath).matches()) {
                return false;
            }

            if (instruction == null) {
                return true;
            }

            boolean matches = instruction.matches(fullPath) ^ instruction.isNegated();

            if (pathname.isDirectory()) {
                return recursive && matches;
            }

            return matches;

        } else {
            // --- Old logic: name-based matching ---
            if (doNotCopy != null && doNotCopy.matcher(name).matches()) {
                return false;
            }

            if (pathname.isDirectory() && isRecursive()) {
                return true;
            }

            if (instruction == null) {
                return true;
            }

            return instruction.matches(name) ^ instruction.isNegated();
        }
    }
}

Is this what you have in mind?

it would mean something like /foo.txt or maybe ./foo.txt - which should also be compatible with path-matching semantics.

I am not sure if or how the code should handle ./foo.txt or /foo.txt.

Could you maybe create a PR with the code above and maybe add a testcase in /biz.aQute.bndlib.tests/test/IncludeResourceTest.java? since you know exactly what you are expecting.

chrisrueger avatar Sep 16 '25 20:09 chrisrueger

I don't want to wriggle out of this, but tbh working with the gradle-based build of bnd always gives me a head-ache... I'm really used to working with Maven...

Also, I don't know if there is an ant-style matcher in the bnd code. If I read the documentation correctly, e.g. the @ unroll selector uses a regex. So although I think an ant-styler matcher would be best, maybe a regex would be more consistent with other parts of the bnd implementation...

reckart avatar Sep 16 '25 20:09 reckart

Also, I don't know if there is an ant-style matcher in the bnd code.

In bnd usually everything is a Glob-Expression. see e.g. https://bnd.bndtools.org/macros/glob.html or https://bnd.bndtools.org/commands/find.html

But I saw that there is FileSet.java which mentions Ant/Gradle in the description

https://github.com/bndtools/bnd/blob/5d699c7550deb6ab65583fe69ef8bc8e7d0225e9/aQute.libg/src/aQute/lib/fileset/FileSet.java#L13-L17

But FileSet is unfortunatelly not used by

https://github.com/bndtools/bnd/blob/5d699c7550deb6ab65583fe69ef8bc8e7d0225e9/biz.aQute.bndlib/src/aQute/bnd/osgi/Builder.java#L1250-L1254

So one could think about how to change the caller of aQute.bnd.osgi.Builder.resolveFiles(File, FileFilter, boolean, String, Map<String, File>, boolean) in a way that it uses FileSet.

chrisrueger avatar Sep 16 '25 22:09 chrisrueger

but tbh working with the gradle-based build of bnd always gives me a head-ache... I'm really used to working with Maven...

@reckart I took your comment to give a Maven Tycho build a try: https://github.com/bndtools/bnd/pull/6814 Would that help you?

chrisrueger avatar Sep 16 '25 22:09 chrisrueger