simplesurance / baur

An incremental task runner for mono repositories.
GNU General Public License v2.0
362 stars 11 forks source link

Add a Build.Input.Apps options #154

Open scorsi opened 4 years ago

scorsi commented 4 years ago

Hello,

It's could be cool to refer to another app without specifying its own used path. I think example is better than talking.


The shared app:

name = "shared"
[Build]
  command = "..."
  [Build.Input]
    [Build.Input.GitFiles]
      paths = ["..."]

The depending app:

name = "my-service"
[Build]
  command = "..."
  [Build.Input]
    [Build.Input.GitFiles]
      paths = ["..."]
    [Build.Input.Apps]
      apps = ["shared"]

Instead of:

name = "my-service"
[Build]
  command = "..."
  [Build.Input]
    [Build.Input.GitFiles]
      paths = ["..."] # + BuildInput.GitFiles of shared app relative to the actual directory
    # + other BuildInput of shared app relative to the actual directory

So when the BuildInput.GitFiles (and every other BuildInput.x of course) of the shared project will change, it will build the shared app and after that my-service app.


I'm not talking about managing dependencies, but look that feature like a trigger which my-service hook on the shared app. If shared app did change, others hooked apps too.

I don't know how easy it can be done in your codebase or if it can be done.

Thanks,

fho commented 4 years ago

Howdy,

I wonder if the shared project is also build by baur. If yes, could you elaborate on what kind of shared project it is, that other projects depend on the build inputs of the shared project instead of it's output?

If the shared project is not build by baur, it sounds like it could be achieved with an include file. We have some libraries in our monorepository that are included from multiple Apps during build time. To prevent that we have to specify the same inputs multiple times, we create an include file that defines those Build Inputs and include that file in the apps.

scorsi commented 4 years ago

I wonder if the shared project is also build by baur. If yes, could you elaborate on what kind of shared project it is, that other projects depend on the build inputs of the shared project instead of it's output?

The dependencies are made thanks to gradle with a simple compile project(":shared"). So if my-service need shared, it will build it by itself.

The problem is, my my-service app did watch for its own inputs and if the shared app is rebuilt, my-service did not.

I have quite a lot of inter-dependencies inside my mono-repo.

If the shared project is not build by baur, it sounds like it could be achieved with an include file. We have some libraries in our monorepository that are included from multiple Apps during build time. To prevent that we have to specify the same inputs multiple times, we create an include file that defines those Build Inputs and include that file in the apps.

It's what I have now. But it's very verbose and it doesn't look like anything anymore.

What I'm proposing could correct that : trigger builds after the hooked apps did build and allow retrieving the baur output (complicated) or just watch the same inputs than the given apps and so let the user handle inter-dependencies (maybe less complicated ?).

fho commented 4 years ago

Yes, I understand, having to manage a lot of input definitions and includes can become quite complex and also error-prone.

Another solution could be to have a build input resolver, similar to BuildInput.GolangSources, for that kind of project / programming language. baur runs the InputResolver and it returns a list of all source files that are required for the build. To make it flexible, we could have a Generic Input Resolver that runs a command which prints filepaths and baur uses them as inputs. This would fit well with the current approach of baur. Would that work for you? Does gradle support to get a list of all source files of a project?

I would like to avoid implementing dependencies/triggers between apps. It would increase the complexity a lot, there are other build tools that follow a related approach like plz, bazel, pants and internally we currently do not have a need for it.

scorsi commented 4 years ago

Another solution could be to have a build input resolver, similar to BuildInput.GolangSources, for that kind of project / programming language. baur runs the InputResolver and it returns a list of all source files that are required for the build. To make it flexible, we could have a Generic Input Resolver that runs a command which prints filepaths and baur uses them as inputs. This would fit well with the current approach of baur. Would that work for you? Does gradle support to get a list of all source files of a project?

In fact, as I said, Gradle handles dependencies by itself. So in my case my-service does gradle build to the shared project thanks to the compile project(":shared") statement, as it's Java behind the scene, it will use jars and linked them together (look it like a dynamic library). No need to specify each files.

To be honest, I don't really look at how the Build.Input.GolangSources works since I didn't use that. But as you explained it, yes, recover inputs from something like baur ls inputs $appname looks fine for me since it will mark shared and any other dependent apps as pending.

I would like to avoid implementing dependencies/triggers between apps. It would increase the complexity a lot, there are other build tools that follow a related approach like plz, bazel, pants and internally we currently do not have a need for it.

I totally understand. Baur manage mono-repo not dependencies, it's belong to the user-stuff here

fho commented 4 years ago

@scorsi I don't know much about Gradle and Java builds either.

But as you explained it, yes, recover inputs from something like baur ls inputs $appname looks fine for me since it will mark shared and any other dependent apps as pending.

That I did not had in mind at all, invoking baur from baur to get the build inputs of another app this way would also solve it. Cool idea :-)

I'll explain how builds + Input Resolver worksfor Golang, then the idea might be more clear:

Golang has the command go list. go list can be run for a Golang Project to list all source files, including imports that are part of it (including imported libraries). That output is basically used for Build.Input.GolangSources. Additionally Golang has a compile cache, if multiple projects use the same library the objects are retrieved from the cache and compilation is very fast.

I wonder if a similar approach is possible with Java+Gradle:

scorsi commented 4 years ago

Has Gradle/Java a command to output all files that would be used in the build?

I think it doesn't. In Maven/Gradle you build every files presents in your sourceset, basically src/**/*.{java,kt,groovy,scala,cl,...}. And listing the files present in the sourceset may create issues since we can customize it (it's very rare to see that, but I did) in Gradle.

It's better using Build.Input.GitFiles for that case.

If yes, maybe it also has a build cache so that it does not matter if the compiled Jar of the shared project exist or is created for every application again from the cache?

Gradle already use caches for every dependencies downloaded, presents in the /cache directory in $GRADLE_USER_HOME (recover as :$USER_HOME/.gradle if not set) and also for every java/kotlin/whatever file. Gradle handles it by its-own and it did it very well, no need to specify dependencies/cache since the dependencies are not set like my-dep >= 0.0.0 but like my-dep:0.42.4-SNAPSHOT so if we want to upgrade our deps, we have to modify the build.gradle file...

For the inter-dependencies, it works differently, each project has a build directory where the jar file is written. So if I have project-A depending of shared, shared will be built and then project-A and link the jar file of shared, if I have project-B which also depends of shared no need to build shared since it was already done by project-A but project-B in itself need to be rebuilt to link the new jar file.

In short, looking to the build.gradle + the sourceset (often src/**) of the project is enough and Build.Input.GitFiles works like a charm.