On 23/07/2013, at 3:50 PM, Alex Ruiz <alr...@google.com> wrote: > Another alternative that Xav and I were talking about later today is that, > instead of having the single-pass multi-model resolution, we can just have a > AndroidStudioModel. > > The ToolModelBuilder for this model will do exactly what Studio is doing now: > gets the IdeaProject and iterates through its children (IdeaModel), > retrieving and AndroidModel if applicable. > > The benefits for this alternative would be: > > 1. We don't have to deal with multiple model hierarchies (e.g. GradleModel, > IdeaProject which have a parent-child structure, and AndroidProject that does > not know about this relationship) > 2. We wouldn't have to wait for Gradle 1.8 to do a single-pass multi-model > resolution. We can use still use 1.6 > > I looked at the code and it looks like we can get the > ToolModelBuilderRegistry (to get the IdeaProject and AndroidProject models) > at any point as long we have a Project: > > project.getServices().get(ToolingModelBuilderRegistry.class) > > At the beginning Xav and I talked about having an IDE-agnostic model, but now > it seems that having one (AndroidStudioModel) is not a bad idea :-) > > If we decide that this is the way to go for now (we can revisit the idea of > having the single-pass multi-model resolution in Gradle later on), the only > change that we'll need is adding the method 'File getRootDir' to > GradleProject (last item in my original e-mail.) This change seems pretty > small and straightforward. Do you think it can be included in Gradle 1.7?
Possibly. What would you use it for? > > Regards, > -Alex > > > On Mon, Jul 22, 2013 at 5:14 PM, Xavier Ducrohet <x...@google.com> wrote: > > > > On Mon, Jul 22, 2013 at 4:34 PM, Adam Murdoch <adam.murd...@gradleware.com> > wrote: > > On 23/07/2013, at 5:53 AM, Alex Ruiz <alr...@google.com> wrote: > >> x >> Greetings everyone, >> >> We have, in Android Studio, a use case for retrieving multiple types of >> model, in single pass, from a Gradle project using the Tooling API. The >> following section explains the problem and the proposed solution. We >> discussed this issue and the potential solution with Adam during Gradle >> Summit back in June. Please let us know your thoughts. We plan to contribute >> this change to Gradle. > > That'd be great. > >> >> Thanks, >> -Alex >> >> Problem >> >> Currently, importing a Gradle-based Android project takes a considerable >> amount of time. The reason is that we have to make multiple calls to the >> Gradle Tooling API. For a project with N Android sub-projects, we have to >> make N+1 calls to Gradle. The reason is that an Android project may have a >> mix of Android sub-projects and plain Java ones and we cannot get all the >> Gradle models for such structure in one shot. >> >> What we are doing now is asking for the IdeaProject at the top-level >> project, then we iterate through the models of the returned IdeaProject >> asking for the corresponding AndroidProject, if it doesn’t have it, we >> assume is a Java project. >> >> Solution >> >> We are proposing is single-pass project import: a way to return all the >> models we need, from the Tooling API, in one invocation: >> >> ModelBuilder<GradleProject> builder = >> connection.models(IdeaProject.class, AndroidProject.class); >> GradleProject project = builder.get(); >> // ‘models’ will have instances of IdeaProject and AndroidProject. >> List<Object> models = project.getModels(); >> for (GradleProject child : project.getChildren) { >> // Each child will have its own models. >> models = child.getModels(); >> } > > This is a good plan. There are a couple of things I would tweak - more on > this below. > >> >> Changes to Gradle Tooling API >> >> org.gradle.tooling.ProjectConnection: >> Add method ‘ModelBuilder<GradleProject> models(Class<?>...modelTypes)’. This >> change will allow us to query multiple models type as shown above. > > `GradleProject` currently bundles together a few things about a project - its > place in the hierarchy, some identifying info, and the tasks of the project. > This would mean that for every model query, we'd need to create and configure > the tasks of each project. And this is something we'd like to avoid where > possible. I think it would be better if the API did not imply anything about > the tasks, and instead the tool can ask for them explicitly. > > One solution would be to split up GradleProject into an interface that > defines the structure and some basic information about each project, and into > a separate model (or perhaps models) that provide additional, specific > information about the project. You can then ask for this extra information as > you would for any model of the project. > > So the tasks would be part of another Model that is queried and returned as > part of GradleProject#getModels()? > > > Another potential issue here is that there isn't necessarily a 1-1 > relationship between a Gradle project and an IDE project. There are many ways > you might map a set of Gradle projects to a set of IDE projects. It just so > happens that our IDE plugins current map each Gradle project to its own IDE > project. But you might, for example, want to map the integration tests of > each Gradle project to a separate `IntTest` IDE project. We want to allow the > build some flexibility over how each Gradle project is mapped to the IDE, so > we don't really want to bake a 1-1 mapping into the tooling API. > > There are really several parallel hierarchies here - the set of Gradle > projects, the set of IDEA modules + IDEA project, and the set of Eclipse > projects. There is a relationship between a given Gradle project and IDEA > module, and a given Gradle project and Eclipse project. > > > It seems like the issue with Integration test is always going to be a Gradle > project becoming 2+ projects in the IDE and it's very specific to the > plugin(s) that are applied in a project(*). > > This means that the API of connection.models(IdeProject.class, > AndroidProject.class) is probably still the right one. It just may mean that > some GradleProject are present twice. > When you want to get the true Gradle project hierarchy, you can still do > connection.getModel(GradleProject.class). > > When calling models(), it's up to the Java plugin to return more than one > project for a given Gradle project. > > (*) I say plugins plural here because there could be a combination of plugin > that changes the returned model maybe? > > So I think there are 3 steps: > > 1. Split GradleModel in 2+ models. The current one, minus the task, plus the > model lists. And a new one for the tasks. > 2. Add the Connection.models() feature to the tooling API to get the model in > a single pass, and possible more than one model per project (e.g. the tasks + > the IDEA model for a Java project). > 3. Change the ToolingModelBuilder class to allow returning more than one > GradleProject object from a single project > > Is there a reason to return something different than GradleProject when > querying the non actual hierarchy? The data from that interface is still > relevant I think. > > > >> >> org.gradle.tooling.model.GradleProject: >> Add method ‘List<Object> getModels()’ to GradleProject. This change will >> allows us to retrieve the models available from a project. > > What would be the behaviour when a requested model is not available? It would > be missing from the list? > > I think the GradleProject would be present but its model list would be empty. > > -- > Adam Murdoch > Gradle Co-founder > http://www.gradle.org > VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting > http://www.gradleware.com > > > > > -- Adam Murdoch Gradle Co-founder http://www.gradle.org VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting http://www.gradleware.com