On 07/02/2013, at 4:06 AM, Adam Murdoch <[email protected]> wrote:

> 
> On 06/02/2013, at 9:02 PM, Luke Daley wrote:
> 
>> 
>> On 06/02/2013, at 12:57 AM, Adam Murdoch <[email protected]> wrote:
>> 
>>> 
>>> On 06/02/2013, at 10:45 AM, Luke Daley wrote:
>>> 
>>>> 
>>>> 
>>>> On 05/02/2013, at 23:08, Adam Murdoch <[email protected]> wrote:
>>>> 
>>>>> 
>>>>> On 06/02/2013, at 2:27 AM, Daz DeBoer wrote:
>>>>> 
>>>>>> On 4 February 2013 15:50, Adam Murdoch <[email protected]> 
>>>>>> wrote:
>>>>>> 
>>>>>> On 05/02/2013, at 5:12 AM, Daz DeBoer wrote:
>>>>>> 
>>>>>>> On 4 February 2013 00:07, Adam Murdoch <[email protected]> 
>>>>>>> wrote:
>>>>>>>> Hi,
>>>>>>>> 
>>>>>>>> So, we're planning to have a bunch of 'jvm binaries' that can be built 
>>>>>>>> from
>>>>>>>> various language source sets and other things. There will be a few 
>>>>>>>> different
>>>>>>>> types of binaries, such as class directory binaries and jar binaries,
>>>>>>>> possibly some others.
>>>>>>>> 
>>>>>>>> Something we need to sort out is how to structure the DSL for these
>>>>>>>> executable things. The current plan is to have a single container that 
>>>>>>>> owns
>>>>>>>> all of these jvm binaries, so you might declare something like this:
>>>>>>>> 
>>>>>>>> jvm {
>>>>>>>>   binaries {
>>>>>>>>       mainClasses(ClassesDirectoryBinary) {
>>>>>>>>           … some inputs and other configuration ...
>>>>>>>>       }
>>>>>>>>       mainJar(JarBinary) {
>>>>>>>>           … some inputs and other configuration …
>>>>>>>>       }
>>>>>>>>   }
>>>>>>>> }
>>>>>>>> 
>>>>>>>> There might be a similar container for native binaries:
>>>>>>>> 
>>>>>>>> native {
>>>>>>>>   binaries {
>>>>>>>>       windowsX86DebugShared(SharedLibraryBinary) {
>>>>>>>>           … some inputs and other configuration …
>>>>>>>>       }
>>>>>>>>       windowsX86DebugStatic(StaticLibraryBinary) {
>>>>>>>>           ...
>>>>>>>>       }
>>>>>>>>       windowsX86DebugExe(ExecutableBinary) {
>>>>>>>>           …
>>>>>>>>       }
>>>>>>>>   }
>>>>>>>> }
>>>>>>>> 
>>>>>>>> Some questions:
>>>>>>>> 
>>>>>>>> * Is using a flat name the best way to identify these things? Once you 
>>>>>>>> add a
>>>>>>>> few dimensions, the names start to get awkward. This is certainly can 
>>>>>>>> be the
>>>>>>>> case for native binaries, and can also be the case for jvm binaries. 
>>>>>>>> For
>>>>>>>> example, I might have (feature, binary type, groovy version, jvm 
>>>>>>>> version) as
>>>>>>>> relevant dimensions for a Groovy library that targets multiple groovy
>>>>>>>> versions and jvm versions.
>>>>>>> 
>>>>>>> Are the names of these things important at all? Or in general are we
>>>>>>> just forcing users to come up with a name that adds little value?
>>>>>> 
>>>>>> I think it varies for different types of things. For some things, a name 
>>>>>> is a natural way of identifying the thing. For other things (most 
>>>>>> things?) it makes more sense to identify a thing by its type and some 
>>>>>> attributes about the thing.
>>>>>> 
>>>>>> The complication is that the set of attributes that identify a thing 
>>>>>> vary based on what I'm building. For example:
>>>>>> 
>>>>>> * If I have a single publication, then I want to refer to it as 'the 
>>>>>> publication'. The other stuff (type, groupId, artefactId, version) are 
>>>>>> just attributes of the publication.
>>>>>> * If I publish 2 maven modules, then I want to refer to them as the 'api 
>>>>>> publication' and the 'impl publication', say.
>>>>>> * If I build debug and release variants of my windows executable, then I 
>>>>>> want to refer to them as the 'debug executable' and the 'release 
>>>>>> executable'. All the other stuff (windows, amd64, multi-threaded, 
>>>>>> visual-c++ compiler, optimisation-level) are just attributes of the 
>>>>>> publication.
>>>>>> * If I build debug and release variants on windows and linux for x86 and 
>>>>>> amd64, then I want to refer to them using a tuple such as (windows, 
>>>>>> amd64, release).
>>>>>> 
>>>>>> That is, a thing often just has a bunch of attributes, any of which 
>>>>>> could be used to identify it, and it's how the thing is different to the 
>>>>>> others that is useful for identifying it.
>>>>>> 
>>>>>> Right, so it "name" just another one of those ways of identifying? 
>>>>>> Sometimes I want to give something a meaningful name, sometimes forcing 
>>>>>> me to come up with a name is a pain in the ass.
>>>>>> 
>>>>>> One nice aspect of ditching the name is that a thing can more naturally 
>>>>>> live in different containers and be grouped in different ways. Which 
>>>>>> would mean that some of these questions about how things are grouped 
>>>>>> become less important - just group them whichever way you like.
>>>>>> 
>>>>>> 
>>>>>>> How
>>>>>>> often does a user need to differentiate between them by name?
>>>>>> 
>>>>>> There are a few main reasons, I think:
>>>>>> 
>>>>>> 1. To configure something that some other logic (a plugin, say) has 
>>>>>> already defined.
>>>>>> 2. To configure the tasks that do work with the thing (compile it, 
>>>>>> generate the pom.xml for it, publish it).
>>>>>> 3. To find the thing to use it as input for some other thing.
>>>>>> 4. To refer to the thing before the 'identifying' attributes have been 
>>>>>> calculated. For example, to refer to a publication before the version 
>>>>>> has been calculated.
>>>>>> 
>>>>>> None this necessarily requires a name - this is just what the name is 
>>>>>> used for at the moment.
>>>>>> 
>>>>>> And I'm not sure any of these are the 'standard' case either. Again I 
>>>>>> refer to repositories: imagine that we used the new "name(Type)" syntax. 
>>>>>> Users would be forced to come up with a name for each of their 
>>>>>> repositories, which would likely not be used elsewhere. Instead, we give 
>>>>>> the ability to supply a name _if_ they want to refer to the repository 
>>>>>> elsewhere.
>>>>>> 
>>>>>> One thing that concerns me about the "name(Type) {}" syntax is that it's 
>>>>>> possibly trickier to document, and trickier for users to grok what's 
>>>>>> going on. In some cases it might make for a cleaner DSL, but I'm not 
>>>>>> certain it's worth the cost.
>>>>>>> We could consider a DSL similar to the repositories syntax:
>>>>>>> 
>>>>>>> jvm {
>>>>>>>   binaries {
>>>>>>>       classes {
>>>>>>>           name "main" // optional
>>>>>>>           … some inputs and other configuration ...
>>>>>>>       }
>>>>>>>       jar {
>>>>>>>           ... we generate a sensible name ...
>>>>>>>           … some inputs and other configuration …
>>>>>>>       }
>>>>>>>   }
>>>>>>> }
>>>>>>> 
>>>>>>> It's possible that we treat this as a standard pattern, whereby a
>>>>>>> NamedDomainObjectContainer could support both with some sort of DSL
>>>>>>> magic:
>>>>>>> 
>>>>>>> container {
>>>>>>>     name(Type) {}
>>>>>>>     subtype { // generated name }
>>>>>>> }
>>>>>>> 
>>>>>>> Or maybe get rid of the 'name' method altogether, and go with:
>>>>>>> 
>>>>>>> // In all cases the added element must provide a unique name, which
>>>>>>> may or may not be configured explicitly.
>>>>>>> container {
>>>>>>>      generalType(SubType) {} // eg 'publication' for 'publications'
>>>>>>> container, or 'dependency' for 'dependencies' container.
>>>>>>>      subType { } // eg 'ivy' for 'publications' or 'project' for
>>>>>>> 'dependencies'
>>>>>>> }
>>>>>> 
>>>>>> These are both interesting options for defining things. One question is 
>>>>>> how do I get something out again, to either configure it or use it?
>>>>>> 
>>>>>> There would be options:
>>>>>> container.findOne({attrib == "value"})
>>>>>> container.findOne(attrib1: "value", attrib2: "value")
>>>>>> container['name']
>>>>>> container.name
>>>>>> 
>>>>>> Note that I'm not suggesting doing away with "name" altogether, but 
>>>>>> instead making it optional.
>>>>> 
>>>>> It might be interesting to push this further, and make name a decoration 
>>>>> of some kind. We've already discussed here a few cases where sometimes 
>>>>> name is relevant and sometimes its not. This isn't a function of the type 
>>>>> of thing, but it is instead a function of how the thing is used. Here are 
>>>>> some other cases:
>>>>> 
>>>>> * Sometimes a piece of code is used as a task and sometimes as an action. 
>>>>> A task is really just an action with a name. The name allows us to do 
>>>>> some useful stuff with the piece of code (e.g. track its history, declare 
>>>>> dependencies and so on), but sometimes we don't care about this useful 
>>>>> stuff.
>>>> 
>>>> The task name is also the primary interface between the user and Gradle.
>>> 
>>> Indeed. This is part of the 'useful stuff'.
>> 
>> Point taken, but I think it's worth pointing out that this is beyond 
>> fundamental to the way that Gradle works currently.
>> 
>>>>> * When using, say, a JavaSourceSet as an input, we don't care about the 
>>>>> name of the source set. We just care that it can describe some source 
>>>>> files and compile dependencies. If we keep name off JavaSourceSet, we 
>>>>> allow other interesting implementations that can be used as input (but 
>>>>> not necessarily output) without forcing each one to have an arbitrary 
>>>>> name.
>>>> 
>>>> How do we require names for this now?
>>> 
>>> Because these things (sometimes) need to be buildable, and to build 
>>> something we currently need a name for it. Whereas to consume something, we 
>>> don't need an identity if we have an object reference to the thing.
>> 
>> I still don't get it. There are all kinds of unnamed buildable things, e.g. 
>> file collections.
>> 
>>>>> * Coming from the other direction: Some of our domain objects are defined 
>>>>> using attributes other than a name. For example, dependencies are defined 
>>>>> using (group, module, version). However, these are treated as the 
>>>>> identifier of the dependency and cannot be changed, even though its quite 
>>>>> ok that these are changed, up to the point that they are consumed.  In 
>>>>> other words, they're just attributes of the dependency. Having a 
>>>>> consistent way to define domain objects in terms of their attributes, and 
>>>>> making identity a decoration, would mean dependencies and publish 
>>>>> artefacts can be defined and used in the same way as everything else.
>>>> 
>>>> 
>>>> 
>>>>> 
>>>>> Putting together a few ideas from this thread (this DSL isn't quite 
>>>>> right, but should give the idea):
>>>>> 
>>>>> // defines a NativeExecutable, with a generated name. With some AST magic 
>>>>> the name might be 'someNativeBinary'
>>>>> def someNativeBinary = items.nativeExecutable { os 'windows'; 
>>>>> architecture 'amd64'; debug: true }
>>>> 
>>>> I know it's not the point but we should be _very_ care about introducing 
>>>> any more ASTs. There use can be very confusing for users and could make 
>>>> IDE support even more difficult.
>>> 
>>> Absolutely. It needs to be worth it.
>>> 
>>> I don't see this particular transform as overly risky. The IDE can infer 
>>> the return type of items.nativeExecutable()
>> 
>> How could it infer it? 
>> 
>> I can see how it might be possible with sophisticated flow analysis. But 
>> that would mean the IDE needs to know which plugins have been applied at 
>> which point in the script and which factories they add to “items”.
>> 
>>> and hence the type of someNativeBinary just fine. It doesn't introduce any 
>>> new syntax. It just takes advantage of an otherwise quite natural syntax, 
>>> ie this statement would work just fine without the transform.
>> 
>> I'm not convinced, but I don't think it matters right now.
>> 
>>>>> // defines an IvyPublication, with a provided name
>>>>> def myPublication = items.ivyPublication { name 'main'; organisation: 
>>>>> 'my-org'; module: 'my-module' }
>>>> 
>>>> So items is just a factory?
>>> 
>>> Maybe. There are 2 parts: creating things and finding things. Maybe `items` 
>>> can do both, maybe there are 2 separate things.
>> 
>> It at least needs to be the graph. I would think factory like behaviour 
>> would be a convenience and not fundamental. Actually, more correctly, it 
>> needs to be a query engine for the graph. The graph is already there in the 
>> connections between objects, we just need a way to dig out parts.
>> 
>>>>> // do some things with the publication
>>>>> myPublication.revision = '1.2'
>>>>> publishing.publications << myPublication
>>>> 
>>>> Why would there even be a publications container? Couldn't you just query 
>>>> the items graph for all of the publications?
>>> 
>>> Good question. Currently, the publications container declares the purpose 
>>> or role of a publication. When it's in the container, its a public output 
>>> of the project. When it's not, it's a publication used for some other 
>>> (undisclosed) purpose and we can't infer anything about it beyond how to 
>>> build it.
>> 
>> This could be a characteristic of the publication itself, not of its 
>> context. Then finding the “public” publications just becomes a more refined 
>> query.
> 
> It might be interesting to combine this with the inferencing we've been 
> talking about, so that any built item marked as 'published' ends up in the 
> right publication in the right repository. For example:
> 
> apply plugin: 'publishing'
> apply plugin: 'maven-publish'
> 
> publishing { repositories { maven { url 'http://myserver' } } }
> 
> def mainJar = …. some kind of lookup ….
> mainJar {
>     publishedAs groupId: 'myGroup', artifactId: 'myArtifact' // with implicit 
> version
> }

Seems like it might be better to think about items only having knowledge of 
their upstream. In the above example you have a jar knowing that it can be 
published (that this might have come from an extension type thing doesn't 
change things I think).

Here's the kind of thing I'm mentally playing with…

create(MavenRepository) { url 'http://myserver' }

create(MavenPublication) { 
        id groupId: 'myGroup', artifactId: 'myArtifact' // with implicit version
        component  find(JavaLibraryComponent, { /* some predicate(s) for 
getting the right one */ })
        publishTo findAll(MavenRepository)
}


> 
> Then, we can infer something like:
> 
> * There is a Maven publication `myGroup:myArtifact:${project.version}` 

How are you inferring it's a maven publication? The existence of a maven 
repository?

> * The main jar (and its meta-data) should be included in this publication.
> * This publication should be published to the Maven repository 
> `http://myserver`

What is this inference based on? The fact that there is only one maven 
repository?

> * This publication can also be published to maven local.
> * All the other stuff that can be inferred about a Maven publication.






-- 
Luke Daley
Principal Engineer, Gradleware 
http://gradleware.com


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply via email to