Tom Eyckmans wrote:


2009/3/18 Adam Murdoch <[email protected] <mailto:[email protected]>>



    Tom Eyckmans wrote:
    Hi,

    During aSkype session on sunday (Hans & me) we thought of
    introducing a SourceDirectory concept to improve our
    compile/resource and test handling.


    I think this is a fantastic idea.

cool


    some of my first ideas:

    # Defining source & resource dirs:

    sourceDir('main') {
       dir('src/main/java')
    }
    resourceDir('main') {
       dir('src/main/resources')
       replaceTokens ( prefix : '${', suffix : '}' ) ( [
          tokenOne: 'valueOne',
          tokenTwo: 'valueTwo'
       ])
    }
    testSourceDir('main') {
       dir('src/test/java')
    }
    testResourceDir('main' {
        dir('src/test/resources')
    }

    a more advanced example (with jdk version  specific code):

    sourceDir('main') {    dir('src/main/java')      }
    sourceDir('jdk14') {    dir('src/jdk14/java')      }
    sourceDir('jdk15') {   dir('src/jdk15/java')      }

    sourceDirGroup('jdk14'){    sourceDirs('main','jdk14')      }
    sourceDirGroup('jdk15'){    sourceDirs('main','jdk15')      }

    testSourceDir('main') {        dir('src/test/java')       }
    testSourceDir('jdk14') {       dir('src/test-jdk14/java')      }
    testSourceDir('jdk15') {       dir('src/test-jdk15/java')      }

testSourceDirGroup('jdk14') { testSourceDirs('main','jdk14') } testSourceDirGroup('jdk15') { testSourceDirs('main','jdk15') }


    Some comments/questions:


Some more questions/comments:

- how do we deal with generated source/resources?

- how do these declarations interact with the convention for source directories? Currently, if I don't declare anything, I can put my source files under src/main/java or src/main/groovy and it all just works. It would be very nice if things continued to work this way. How, then, would I declare that I want something other than src/main/java as my source directory? What if I want to use src/main/java, but want to change the includes/excludes?

I think we want to come up with a pattern for how default domain objects should work. There are severa places where we add a default domain object, such as the source dir, test source dir, jar archive and test suite. And plenty more where we could, such as a default distribution, repository, documentation bundle, and so on.

The question is, how will we allow these default domain objects to be used if nothing is specified, but allow the build file to declare that the default should not be used, or that the default should be changed in some way?

    How would we deal with resources and source living in the same
    directory?

I think the solution to this would be to type the sourceDirs like we do with artifact tasks, the type would define includes/excludes.

production and test classes in the same directory? java 1.4 and java 1.5 classes in the same directory?

I think both are really nasty situations that should be avoided at all times (compile/testCompile configuration concept is useless) but in the case that it happens, I think we should just compile it twice and don't treat it in a special way. Best possible scenario would be to define the sourceDirs/testSourceDirs with includes/excludes (packaged based/ or classname based (again abstract class issues probably when not following a naming convention)) otherwise I think it is a lost cause and it really ought to be separate dirs anyways :).

I think providing includes/excludes will work fine.


    How would we deal with a multi-language project, which has both
    java and groovy source, or java and c source, or whatever?

I think by typeing the sourceDirs we have a good handle on multi-language projects, for groovy source the type would extend the java type and include both java/groovy sources, I wouldn't go for multi-typed sourceDirs because we would have to figure out a pretty difficult regexp expression merger (and I'm not nearly smart enough for that :)).

So, something like this?

groovySourceDir('main') {
   ...
}

Then sourceDir should probably be called javaSourceDir.



    Why do we need source dir groups?

In case of the following sourceDirGroup:

sourceDirGroup('jdk14'){ sourceDirs('main','jdk14') }
You include multiple sourceDirs
- main
- jdk14

By grouping these source dirs you don't need to mention both dirs when referencing the group.

Why don't we just use a groovy List or Set?



    # Define how stuff should be compiled

    // default by name and synhtetic
    compile('main') {
       // options
    }
// or compileMain {
       // options
    }

    // custom compiles (naming optional)
    compile ( sourceDir('build/generated-java-sources') ) {
        // options
    }
// or compile ('antlr-source-compile',
    sourceDir('build/generated-java-sources') ) {
        // options
    }


    What are these defining? compile tasks? some kind of domain object?


both a domain object and a task (more info in reply to the more generalized idea)

We should choose one or the other. Either you define the domain object, and zero or more synthetic tasks are created for it, or you define a task, which has-a domain object of the appropriate type. I think domain object is almost always the way to go.

The names of the domain object should be nouns, and the tasks should be verbs. What you're defining above could be thought of as a ClassDirectory, analogous with a SourceDirectory, and produced by compilation, instrumentation, weaving, exploding a jar, etc.




    # Define how stuff should be tested:

    // default by name and synhtetic
    test('main') {
     // options
    }
    // or
    testMain {
     // options
    }

    // custom compiles (naming optional but required when you want to
    be able to use it from the command line)
    test (sourceDir('main'), testSourceDir('main') ) {
      dependsOn(...) ?
      // options
    }
    // with explicit name
    test ('slow-test', sourceDir('main'), testSourceDir('main') ) {
      // options
    }


    what are these defining?

both a domain object and a task (more info in reply to the more generalized idea)

I think what you're defining here is a TestSuite. And a TestSuite has a synthetic task to execute it, and possibly a task to generate the report for it.



    # Define how stuff should be javadoc-ed

    javadoc('main') {
        // options
    }
    // or
    javadocMain() {
      // options
    }

    // custom javadoc
    javadoc('custom', sourceDirs('jdk14','antlr-sources') ) {
      // options
    }


    what are these defining?

both a domain object and a task (more info in reply to the more generalized idea)

I don't think we should bother with a domain object for API documentation, yet. Let's leave it until we get the more basic compilation, testing and bundling stuff looking better. We should just stick with the Javadoc and Groovydoc tasks.



    # Define how stuff should be packaged

    defaultJar(JarTask, name: project.name <http://project.name>) {
         mainCompile {  // include the result of main compile
             // additional options includes/excludes ?
         }
         mainResources // include the result of main resources
    }
    // or
    defaultJar(JarTask, name: project.name <http://project.name>) {
          main // all of main compile + resources
    }

    defaultSourceJar(JarTask, name: project.name
    <http://project.name>, classifier: 'sources') {
        mainSources
    }

    defaultJavadocJar(JarTask, name: project.name
    <http://project.name>, classifier: 'javadoc' ) {
        javadocMain
    }

    defaultTestJar(JarTask, name: project.name <http://project.name>,
    classifier: 'tests') {
        mainTestCompile
        mainTestResources
    }
    // or
    defaultTestJar(JarTask, name:project.name <http://project.name>,
    classifier: 'tests') {
        mainTest // all of mainTest test compile + test resources
    }

    defaultTestSourceJar(JarTask, name: project.name
    <http://project.name>, classifier: 'test-sources') {
         mainTestSources
    }

     // this way it is very easy to define bundles:
    gradleExternalJavadocBundle(JarTask, name:
    'gradle-external-javadoc' ) {
main { package('org.gradle.external.javadoc')
          }
          manifest {
               // additional manifest instructions
          }
    }


    We will need some way to include the these things with a prefix,
    as not everything ends up in the root of an archive.


I agree, just a tought:

defaultJavadocJar(JarTask, name: project.name <http://project.name>, classifier: 'javadoc' ) {
    docs { // if not defined in this context it is a directory
        'api-docs' {
            javadocMain
       }
    }
}

I like this.



    I think this is a very powerfull concept to use the task names to
    include the result of a compile/resources task in an artifact and
    I find it very natural but other views may differ.


    I agree. In fact, we should generalise it: you should be able to
    add any domain object which implements FileCollection (say) to an
    archive. Given that Configuration, Dependency, SourceDirectory,
    Archive, and all file producing tasks implement FileCollection,
    this makes it very easy to assemble archives.

I'm not sure if adding the FileCollection stuff on tasks is the best way to go, I think tasks have a very clear responsibility that would get cluttered,

The clear responsibility of almost every task in existence is to produce a set of files. Why should a task not declare what files it will produce? To me, it seems quite remarkable that Gradle, as a general purpose build tool, does not model this most basic of build concepts at all.

I think there should (optionally) be a has-a relationship between a Task and the FileCollection (or collections) it produces. That FileCollection may be a domain object, or may not be. This allows us to do some interesting things in the intrastructure: tasks can declare dependencies on artifacts rather than tasks (which is a more accurate model for most tasks), we can skip tasks whose output already exists, you can request from the command-line that an artifact be built.


Adam

Reply via email to