I'm trying to build an Amper plugin that uses sour...
# kotlin-toolchain
c
I'm trying to build an Amper plugin that uses source code analysis of one module to feed another module: Where
ui
is the module that uses the generated source code from
build-config
after analyzing the classes of the
web
module: The problem is that I cannot use the templating variables to CHOOSE which module to analyze and which module to register resources with. The naive approach of using the auto-completed variables runs into a loop error (which makes sense):
Copy code
# build-config/plugin.yaml
tasks:
  generate:
    action: !generateTypescriptApiClient
      generatedSourceDir: ${taskOutputDir}
      classpaths:
        # Understandable why I cannot use the module templating variables here, as they are not resolved yet.
        - ${module.compileClasspath}
        - ${module.runtimeClasspath}
    markOutputsAs:
      - path: ${action.generatedSourceDir}
        kind: jvm-resources
However, I would expect overriding to work:
Copy code
# ui/module.yaml -- Does not work
plugins:
  build-config:
    enabled: true
    classpaths:
      - ${web.compileClasspath}
      - ${web.runtimeClasspath}
This is what I get with the above:
Copy code
➤ ./amper --version
JetBrains Amper version 0.9.2 (80094bb, 2025-12-11)
➤ ./amper build
00:01.204 INFO                            Processing local plugin schema for [build-config]...
00:03.420 ERROR                           /Users/nthalk/local/src/iodesystems/agent/ui/module.yaml:24:5: Unknown property classpaths
00:03.515 ERROR                           Task dependency loop detected:
1. task `generate` in module `ui` from plugin `build-config` (*)
   ╰───> requests classpath resolution that includes the results from,
         requests classpath resolution that includes the results from
2. compilation of module `ui` <─────────────────────────────────────╯
   ╰───> needs resources from ───────────╮
3. resource generation for module `ui` <─╯
   ╰───> includes the directory `<project-build-dir>/tasks/_ui_generate@build-config` generated by
4. task `generate` in module `ui` from plugin `build-config` (*) <───────────────────────────────╯
╰─ Related configuration elements that may have caused the loop:
   ╰─ /Users/nthalk/local/src/iodesystems/agent/build-config/plugin.yaml:6:11
   ╰─ /Users/nthalk/local/src/iodesystems/agent/build-config/plugin.yaml:7:11
   ╰─ /Users/nthalk/local/src/iodesystems/agent/build-config/plugin.yaml:9:9
When I replace
classpaths
in
build-config/plugin.yaml
with
[]
and try to specify module scoped variables in
ui/module.yaml
, I get:
Copy code
# See <https://amper.org/latest/user-guide/plugins/quick-start/#adding-plugin-settings>
...
plugins:
  build-config:
    enabled: true
    classpaths:
      - ${web.compileClasspath}
      - ${web.runtimeClasspath}
Copy code
➤ ./amper build
00:01.170 INFO                            Processing local plugin schema for [build-config]...
00:03.422 ERROR                           /Users/nthalk/local/src/iodesystems/agent/ui/module.yaml:24:5: Unknown property classpaths

ERROR: failed to read Amper model, refer to the errors above
Is there any documentation about using one module to generate resources for another module via a plugin? I assume this is a common case.
FYI, I'm working on a client generator for multiplatform projects using a typescript generator metadata (for react clients I built) to ktorfit annotations: https://foso.github.io/Ktorfit/requests This should enable autogenerated, typesafe spring api clients in multiplatform projects.
a
hi there thanks for reaching us out
However, I would expect overriding to work:
This kinda makes sense that you think that might work But I'd still would rather learn from you, where it comes from, that would be very insightful data to refine our expectations about what is intuitive and what isn't I suspect you have a dependency loop because module's
compileClasspath
requires resources to be processed already, and you mark output of the action as
jvm-resources
@Fedor Ihnatkevich please confirm or disprove me considering your use case, I don't think it's supported now: even if you create a new module with the dependency to
web
and use module's
compileClasspath
and generate some artifact, other module couldn't consume it from
ui
module as a separate classpath, so either it would be part of the whole classpath (including
web
which might be a workaround) or there is no way... for now at least, I might be wrong, so cc @Fedor Ihnatkevich In any way, it would be great to understand your use case better could you please elaborate a bit? what do you mean by typescript generator metadata? what is your
web
app? is it a serverside? what your
generateTypescriptApiClient
does exactly? If the artifact of your generation isn't consumed by Amper in any way, then, I believe you need to apply plugin to
web
module directly and don't mark output, I think that should be enough
f
Hi, Carl! I think you are very close to getting it right. I see that you linked the article
Adding plugin settings
(this one). Are you sure you have set your settings interface in plugin's module.yaml as shown in the docs:
Copy code
pluginInfo:
  settingsClass: com.example.Settings
Then you have to reference these settings via
${pluginSettings.classpaths}
in your task, like
classpaths: ${pluginSettings.classpaths}
or better just pass the whole settings object into the task action directly and reference it like
settings: ${pluginSettings}
. Configuring tasks directly from the
module.yaml
is not possible - tasks are implementation detail; the plugin configuration has to go through the
pluginSettings
. Hope this helps. If you still have questions, feel free to ask them!
c
@Anton Prokhorov, yeah, the issue is a classpath loop - because I cannot read the classpath to then generate sources. This is an issue for generated code based on the project - though, it is NOT clear how to take ONE module, and generate jvm-resources in ANOTHER. @Fedor Ihnatkevich, I see, but the comment on the page:
Copy code
It is not yet possible to use references (${...}) in module.yaml files or access the module configuration tree from plugin.yaml. We are planning on supporting this in some quality in the following releases.
Makes it impossible to do it currently. All I'm looking for is some sort of way to generate resources/sources from one module into another, and without the classpath references from other modules, it looks hard to do. Think of all the common api generators (typescript, openapi), database binding generators (jooq), security rule generators problem scenarios that many gradle plugins are created to solve. Showing how to do something like this in the docs would rapidly increase the adoption for users who require these steps - I have since abandoned my PoC for amper, since gradle allows me to do this with ease due to the inputs/output spec of the gradle tasks.
f
@Carl Taylor Thanks for the feedback! Yeah, we'll try to improve the docs and samples according to the feedback of what is understandable and what causes confusion, like this case here. If we return to your use-case: >
It is not yet possible to use references (${...}) in module.yaml
> Makes it impossible to do it currently. You don't need references in module files to achieve what you need. > how to take ONE module, and generate jvm-resources in ANOTHER A plugin always contributes stuff in the module it is applied to. So you apply the plugin to the "target" module. And to reference the "source" module, you expose a
sourceModule: Dependency.Local
setting in the plugin settings, and pass the "source" module to it, like this:
Copy code
# target/module.yaml
plugins:
  my-codegen-plugin:
    enabled: true
    sourceModule: ../source-module
And then on the plugin side:
Copy code
# my-codegen-plugin/plugin.yaml
tasks:
  generate:
    action: !myGenerateAction
      # ...
      compileClasspath: 
        dependencies: [ ${pluginSettings.sourceModule} ]
        kind: compile
      runtimeClasspath: 
        dependencies: [ ${pluginSettings.sourceModule} ]
        kind: runtme
     # markOutputsAs: ...
You don't need to write something like
${(../source-module).runtimeClasspath
(which is not possible anyway now) you just use full form for the
Classpath
spec on the plugin side. I'll see how to improve the docs (or the API) so that it's more clear how to implement cases like this.