Does KSP supports loading resource files from the ...
# ksp
u
Does KSP supports loading resource files from the classpath? In a Java annotation processor, I could use the Filer's method getResource() (see https://javadoc.io/static/org.jvnet.sorcerer/sorcerer-javac/0.8/index.html?com/sun/tools/javac/processing/JavacFiler.html), which supports loading resources from CLASS_OUTPUT, CLASS_PATH and ANNOTATION_PROCESSOR_PATH. For context, I have a KSP processor that loads a template for the sources it generates, which is located in one of the jars in its classpath.
I was able to get that functionality by creating and configuring my own
JavacFileManager
and getting the resources from it. For that to work, I needed to initialize a
JavacFileManager
with the current classpath, which KSP doesn't provide. To achieve that, I need to pass the classpath into my KSP processor using a custom argument. Here's a working code for my
KspFiler
(sorry for the Java code 🙂 ):
Copy code
import javax.annotation.processing.Filer;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.main.Option;
import com.sun.tools.javac.processing.JavacFiler;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Options;

public class KspFiler implements Filer {

  private CodeGenerator codeGenerator;
  private Resolver resolver;
  private JavacFiler javacFiler;

  public KspFiler(CodeGenerator codeGenerator, Resolver resolver) {
    this.codeGenerator = codeGenerator;
    this.resolver = resolver;

    Context context = new Context();
    JavacFileManager.preRegister(context);
    JavacFileManager fileManager = (JavacFileManager) context.get(JavaFileManager.class);

    Options localOptions = Options.instance(context);

    // "classpath" is retrieved through a KSP param
    localOptions.put("CLASS_PATH", classpath);

    // Getting the available javac Option objects (java8 != java9+)
    Set<Option> javacAvailableOptions = Option.getJavacFileManagerOptions();
    
    for (Option option : javacAvailableOptions) {
      String value = localOptions.get(option.toString());
      if (value != null) {
        // Setting the option in the file manager
        handleOptionJavac9(fileManager, option, value);
      }
    }

    // Creating JavacFiler using the file manager we initialized
    JavacProcessingEnvironment env = JavacProcessingEnvironment.instance(context);
    javacFiler = (JavacFiler) env.getFiler();
  }

  private void handleOptionJavac9(JavacFileManager fileManager, Option option, String value) {
    try {
      // Using reflection because this method isn't exposed
      Method handleOptionMethod =
          JavacFileManager.class.getMethod("handleOption", Option.class, String.class);
      handleOptionMethod.invoke(fileManager, option, value);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  // ....

  @Override
  public FileObject getResource(
      JavaFileManager.Location location, CharSequence pkg, CharSequence relativeName)
      throws IOException {
    return javacFiler.getResource(location, pkg, relativeName);
  }
}
👌 1
I wonder though if this is something KSP can support as part of the Resolver. It already has
getDeclarationsFromPackage()
that does a lookup on other dependencies, but only for declarations. What would it take to create a similar API to retrieve resources? This will create more parity with the Filer interface and help migrating existing Java AP more easily.
t
Thanks for bringing this up. Yes, we plan to do it and will prioritize it: https://github.com/google/ksp/issues/431
❤️ 1
m
@Udi Cohen I'm having hard time trying to access com.sun.tools.javac.processing.JavacFiler. It probably depends on some compiler arguments but I cannot understand how to provide them in build.gradle. Can you explain how you did that?