'How to trigger recompilation of a dependant project during a SBT build?

Background

I have a complex code generation project called migrationManager with a recursive code generation strategy:

  • migration1 is used to generate schema1.
  • migration2 uses schema1 to generate schema2.
  • migration3 uses schema2 to generate schema3.
  • etc.

In order to compile migrationManager and since the later migration files depend on files which are not generated yet, the files are brought in sequentially via sbt commands like such:

  1. sbt migrationManager/run update -c This copies the next migration file into the migrationManager project.
  2. sbt migrationManager/run apply and sbt migrationManager/run codegen produce the next code-generated schema.

When executing these manually via the terminal, and since we copy over the next migration file or generate the next schema, sbt detects the change in the source code and recompiles the project when the next run command is issued.

One way to automate this is with the watch version of the commands:

~sbt migrationManager/run update -c; migrationManager/run apply; migrationManager/run codegen

which detects source code changes and re-triggers these commands.

Problem

My goal is to automate this within the build process, so I am writing a task which tries to loop through and run these commands.

The issue I'm running into is that when the next sbt run command is run, it does not detect the source code change and thus does not recompile before running the next command:

lazy val generateSchemas = taskKey[Seq[File]]("Generate database schema code based on migration files.")

// Migration manager receives the code changes from update command
lazy val migrationManager = project.settings(...)

lazy val app = project
  .dependsOn(migrationManager)
  .settings(
    libraryDependencies ++= appDependencies,
    generateSchemas := {
      val cp = (Compile / dependencyClasspath).value
      val r = (migrationManager / Compile / runner).value
      val s = streams.value
      while (migrationsRemain > 0) {
        r.run("migrationManager.Application", cp.files, Seq("update", "-c"), s.log)

        // What can I do here to incrementally recompile migrationManager with the added source file?

        r.run("migrationManager.Application", cp.files, Seq("apply"), s.log)
        r.run("migrationManager.Application", cp.files, Seq("codegen"), s.log)
      }
      Seq(...)
    },
    Compile / sourceGenerators += generateSchemas.taskValue
  )

In the above example, the apply and codegen commands do not work properly because the copied migration file is not compiled into the project.

Question

Is it possible to trigger a recompilation of the subproject migrationManager from within the app project's generateSchemas task I have defined above?

Further Attempts

Looks like I can get hold of migrationManager's compiler via:

val c = (migrationManager / Compile / compilers).value

within the task and from this value I can call compile like so:

c.scalac().compile(...)

which takes in a bunch of parameters:

compile(
  VirtualFile[] var1,
  VirtualFile[] var2,
  FileConverter var3,
  DependencyChanges var4,
  String[] var5,
  Output var6,
  AnalysisCallback var7,
  Reporter var8,
  Optional<CompileProgress> var9,
  Logger var10
): Unit;

but I'm not sure what to use as parameters (nor where I can see a trace of previous calls).

Is there a way to copy the values from the previous compilation of migrationManager so I can trigger it again from within my app project task?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source