On 30 Nov 2013, at 11:07 am, Michael Putters 
<michael.putt...@binarygenetics.com> wrote:

>  
> From: Adam Murdoch [mailto:adam.murd...@gradleware.com] 
> Sent: Thursday, November 28, 2013 11:52 PM
> 
> > 1. Primitive language source sets that contain source files of a given 
> > language. Headers and source files would live in different source sets.
> > 2. A way to declare dependencies between language source sets.
> > 3. A way to combine language source sets of a given type into a composite 
> > source set of the same type.
> > 4. A way to define a logical source set, where each logical source set 
> > represents some logical entity with an API and implementation. A logical 
> > source set would have, by convention, a public headers source set, a shared 
> > headers source set, and a bunch of implementation language source sets. Any 
> > of these can be empty. There would be, by convention, a dependency from 
> > each implementation source set on the shared headers and the public 
> > headers, and a dependency from the shared headers on the public headers. 
> > When you declare a dependency on a logical source set, the only thing you 
> > can see are the public headers.
> > 5. A library component has-a logical source set.
> > 6. An executable component has-a logical source set with no public headers.
>  
> > In other words, it’s just more of the same - a graph of things with 
> > dependencies between them, with some conventional wiring.
>  
> > As far as convention for a component goes, I’d do something like this:
>  
> > - A language source set for each implementation language.
> > - No language private headers. You’d be able to define them if you needed, 
> > by defining a new header source set and adding a dependency from the 
> > specific language source set on the header source set.
> > - Shared header source set contains any header files found in 
> > `src/${component.name}/include` (say)
> > - Public header source set contains ant headers found in 
> > `src/${component.name}/public` (say)
>  
> This graph is getting deep J
>  
> So what would the DSL look like for such a list?

Good question. There’s 4 things the DSL needs to do:

1. Allow you to define a source set of a given type and configure it, e.g. to 
set the source directories or whatever.
2. Allow you to declare dependencies between source sets (and other things).
3. Allow you to combine source sets into a component.
4. Allow plugins to do these things as a convention and allow you to change 
what the convention has done.

One option is to flatten the whole thing out, so that definition and wiring and 
composition are all separate things. For example, to wire up the source for a 
library ‘mylib’ the plugins might do something like:

sources {
    // headers and source files are separate things
    mylibApi(HeaderSet) { 
        srcDirs = ‘src/mylib/public’
    }
    mylibShared(HeaderSet) { 
        srcDirs = ‘src/mylib/include’
        dependsOn mylibApi
    }
    mylibCpp(CppSourceSet) {
        srcDirs = ’src/mylib/cpp’
        dependsOn mylibShared
    }
    mylibC(CSourceSet) {
        srcDirs = ‘src/mylib/c’
        dependsOn mylibShared
    }
    mylib(NativeLibrarySourceSet) {
        api mylibApi
        source mylibCpp, mylibC
    }
}

libraries {
    mylib {
        source = sources.mylib
    }
}

You’d be able to navigate to things via their relationships in order to 
configure them and query them:

libraries {
    mylib {
        sources {
            cpp {
                srcDirs = ‘…’
                dependsOn libraries.someOtherLib
            }
        }
    }
}

To wire in more source sets, you’d do something like:

// a generated source set
sources {
    mylibGeneratedC(CSourceSet) {
        generatedBy someTask
        dependsOn libraries.someLib
    }
    mylib {
        source mylibGeneratedC
    }
}

// some private headers visible only to the C source files
sources {
    mylibCHeaders(HeaderSet) {
        srcDirs = ‘…'
    }
    mylibC {
        dependsOn mylibCHeaders
    }
}

// windows specific cpp
sources {
    mylibWindowsCpp(CppSourceSet) {
        srcDirs = ‘src/mylib/windows/cpp’
        dependsOn operatingSystems.windows
    }
    mylib { source mylibWindowsCpp }
}

That is, most stuff happens at the source set level. This is flexible but 
ignores that, from most people’s point of view, the component is the more 
important concept.

So, another option is to combine definition and aggregation. The plugins would 
do something like:

libraries {
    mylib {
        sources {
            api(HeaderSet) { 
                srcDirs = ‘src/mylib/public’
            }
            shared(HeaderSet) { 
                srcDirs = ‘src/mylib/include’
                dependsOn api
            }
            cpp(CppSourceSet) {
                srcDirs = ’src/mylib/cpp’
                dependsOn shared
            }
            c(CSourceSet) {
                srcDirs = ‘src/mylib/c’
                dependsOn shared
            }
        }
    }
    api = sources.api
}

Each source set could probably be visible as a separate thing via the sources 
container:

sources {
    mylibApi { // tweaks the libraries.mylib.sources.api source set }
}

To wire in more source sets, you’d do something like:

// a generated source set
libraries {
    mylib {
        sources {
            generatedC(CSourceSet) {
                generatedBy someTask
                dependsOn libraries.someLib
            }
        }
    }
}

// some private headers visible only to the C source files
libraries {
    mylib {
        sources {
            cHeaders(HeaderSet) {
                srcDirs = ‘…'
            }
            c {
                dependsOn cHeaders
            }
        }
    }
}

// windows specific cpp
libraries {
    mylib {
        sources {
            windowsCpp(CppSourceSet) {
                srcDirs = ‘src/mylib/windows/cpp’
                dependsOn operatingSystems.windows
            }
        }
    }           
}

This approach seems more natural for most use cases, but is more rigid because 
source sets can only be defined as part of a native component, and this isn’t 
always the reality.

We might also combine the two approaches, so you can define source sets either 
at the top level or as part of a component, or wherever it makes sense, and 
refer to them from either context:

sources {
    someArbitrarySourceSet(CSourceSet) {
        dependsOn libraries.myotherlib
        dependsOn sources.someSourceSet
        dependsOn libraries.mylib.sources.cpp
    }
}

libraries {
    mylib {
        // An alias for 
libraries.mylib.sources.add(project.sources.someArbitrarySourceSet)
        buildFrom sources.someArbitrarySourceSet
    }
   myotherlib {
        buildFrom sources.someSourceSet
   }
}


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