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 :-)

It's an interesting idea, but I don't think it's going to work out, at least 
without some kind of change to the tooling API. When you ask for a model, the 
tooling API looks in the ProjectConnection's default project for something that 
can build the requested model (the ProjectConnection's default project is the 
project in the directory you pass to GradleConnector). A ProjectConnection is 
actually a connection to a single Gradle project (not a Gradle build), and it 
just so happens that most models you can get for a project include information 
about the other projects in the same build.

So, to make an AndroidStudioModel work, you'd need to keep probing projects 
until you found one with an Android plugin applied that could give you such a 
model. It's better than doing N + 1 tooling API requests, but it's not as good 
as a single tooling API request.

To me, the way forward here is to decouple the scope that you're interested in 
(ie which Gradle projects) and the information that you're interested in (ie 
which information for a given project). You can then say: give me these models 
for this set of projects. Adding GradleProject.getModels() is a step in this 
direction, but there are other ways we could tackle this.


> 
> 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?
> 
> 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



Reply via email to