잊지 않겠습니다.

'2013/10/28'에 해당되는 글 2건

  1. 2013.10.28 gradle 정리 - queryDSL code generate (precompile) 1
  2. 2013.10.28 gradle 정리 - code analysis
queryDSL을 이용한 java project의 경우에는 gradle로는 조금 과정이 복잡하게 됩니다. 

queryDSL의 경우에는 pre-compile 과정이라고 해서, 기존의 domain entity 들을 이용한 Q class들을 생성해야지 됩니다.
이런 Q class들을 생성하는 build 과정을 먼저 거친 다음에, 기존의 class들을 compile시키는 과정을 거쳐야지 됩니다. 

따라서, compileJava보다 먼저 선행되는 작업이 반드시 필요하게 됩니다. 

먼저, generate될 code가 위치할 곳을 지정해줍니다. 

sourceSets {
    generated {
        java {
            srcDirs = ['src/main/generated']
        }
    }
}

그리고, 새로운 task를 생성시켜줍니다. 
task generateQueryDSL(type: JavaCompile, group: 'build') {
     source = sourceSets.main.java
     classpath = configurations.compile
     options.compilerArgs = [
          "-proc:only",
          "-processor", "com.mysema.query.apt.jpa.JPAAnnotationProcessor"
     ]
     destinationDir = sourceSets.generated.java.srcDirs.iterator().next()
}

위 코드는 compile type의 code라는 것을 먼저 생성시켜주고, 원 source와 Q code를 생성해주는 pre processor를 지정해주는 코드입니다. 마지막줄에서 code를 어느곳에 생성할지를 결정하고 있습니다.
그리고, compileJava에 종속성을 주입해야지 됩니다. 기존에 java plugin을 사용하고 있기 때문에, task를 새로 생성하는 것이 아닌 overwrite 시키는 개념으로 접근하면 됩니다. 

compileJava {
     dependsOn generateQueryDSL
     source generateQueryDSL.destinationDir
}
compileGeneratedJava {
     dependsOn generateQueryDSL
     options.warnings = false
     classpath += sourceSets.main.runtimeClasspath
}

이 두 task를 통해서, 기존의 compileJava의 경우 반드시 generateQueryDSL task를 실행 후, 동작하게 되고 source는 generateQueryDSL.destinationDir을 참고해서 compile을 행하게 됩니다. 또한 compileGeneratedJava task 역시 generateQueryDSL task를 실행하고, compile 된 객체들을 같이 이용해서 jar를 만들어주는 cycle을 통하게 됩니다. 


마지막으로, project를 모두 clean 시키기 위한 코드 역시 수정이 필요합니다. generated 되는 코드들도 같이 지워주기 위해서는 clean task를 다음과 같이 재정의하는 것이 좋습니다. 

clean {
     delete sourceSets.generated.java.srcDirs
}

다음은 build.gradle 파일의 전체입니다. 참고해보시길 바랍니다. 

sourceSets {
    generated {
        java {
            srcDirs = ['src/main/generated']
        }
    }
}

dependencies {
    def querydslVersion = '3.2.4'
    def slf4jVersion = "1.7.2"

    def queryDSL = '3.2.4'
    compile project(':common')
    compile group: 'mysql', name: 'mysql-connector-java', version:'5.1.22'
    compile group: 'com.jolbox', name: 'bonecp', version:'0.7.1.RELEASE'
    compile group: 'com.google.guava', name: 'guava', version:'14.0'
    compile group: 'org.springframework', name: 'spring-core', version:'3.2.3.RELEASE'
    compile group: 'org.springframework', name: 'spring-orm', version:'3.2.3.RELEASE'
    compile group: 'org.springframework', name: 'spring-tx', version:'3.2.3.RELEASE'
    compile group: 'org.springframework', name: 'spring-context', version:'3.2.3.RELEASE'
    compile group: 'org.springframework', name: 'spring-context-support', version:'3.2.3.RELEASE'
    compile group: 'org.hibernate', name: 'hibernate-core', version:'4.1.10.Final'
    compile group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-2.0-api', version:'1.0.1. Final'
    compile group: 'org.hibernate', name: 'hibernate-entitymanager', version:'4.1.10.Final'
    compile group: 'org.hibernate', name: 'hibernate-validator', version:'4.3.1.Final'

    compile group: 'org.springframework.data', name: 'spring-data-jpa', version:'1.4.2.RELEASE'

    compile group: 'com.mysema.querydsl', name: 'querydsl-core', version:"$queryDSL"
    compile group: 'com.mysema.querydsl', name: 'querydsl-jpa', version:"$queryDSL"
    compile group: 'com.mysema.querydsl', name: 'querydsl-sql', version:"$queryDSL"

    compile "com.mysema.querydsl:querydsl-apt:$queryDSL"
    provided 'org.projectlombok:lombok:0.12.0'

    compile "org.slf4j:jcl-over-slf4j:$slf4jVersion"
    compile "org.slf4j:jul-to-slf4j:$slf4jVersion"

    provided "com.mysema.querydsl:querydsl-apt:$querydslVersion"
    provided 'org.projectlombok:lombok:0.12.0'
}

task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') {
    source = sourceSets.main.java
    classpath = configurations.compile + configurations.provided
    options.compilerArgs = [
            "-proc:only",
            "-processor", "com.mysema.query.apt.jpa.JPAAnnotationProcessor"
    ]
    destinationDir = sourceSets.generated.java.srcDirs.iterator().next()
}

compileJava {
    dependsOn generateQueryDSL
    source generateQueryDSL.destinationDir
}

compileGeneratedJava {
    dependsOn generateQueryDSL
    options.warnings = false
    classpath += sourceSets.main.runtimeClasspath
}

clean {
    delete sourceSets.generated.java.srcDirs
}

idea {
    module {
        sourceDirs += file('src/main/generated')
    }
}


gradle build를 실행하면 이제 Q class들이 모두 compile 되고, jar 안에도 역시 Q class들이 모두 위치한 것을 알 수 있습니다. 



Posted by Y2K
,

gradle 정리 - code analysis

Java 2013. 10. 28. 13:07
code의 품질을 높이기 위해서 자주 쓰이는 방법중 하나는 static java code analysis를 통해서 code의 품질을 평가하는 겁니다. 

자주 쓰이는 방법으로는 checkstyle, jdepend, PMD, findbugs 등이 있고, 이러한 방법들을 체계적으로 잘 관리해주는 tool로는 sonar가 있습니다. 

1. checkstyle

apply plugin : 'checkstyle'

을 사용해서 checkstyle을 사용할 수 있습니다. checkstyle은 사용되는 check rule에 대한 xml이 반드시 존재해야지 되며, 이는 다음과 같이 설정이 가능합니다. 

checkstyle {
    configFIle = file('config/checkstyle/sun_checks.xml')
}

그리고, checkstyle의 결과는 xml 파일로 나오게 되며 이 결과는 jenkins, hudson 등의 CI tool에서 결과를 확인하는데 사용하게 됩니다. 
report path는 기본적으로 build/checkstyle 폴더에 위치하게 되며, 이 폴더의 위치와 xml 파일의 이름은 다음과 같이 설정하면 됩니다. 

checkstyle {
    reportDir = file("${buildDir}/checkstyle-output")
}
checkStyleMain {
    reports {
       xml.destination = file("${checkstyle.reportsDir}/checkstyle.xml")
     }
}

gradle에서 사용하기 위해서는 gradle checkStyleMain task를 구동하면 checkStyle 검사를 행하게 됩니다. 

2. PMD

PMD는 code style에서 발생할 수 있는 error들을 검증하는 방법입니다. checkstyle은 coding에 있어서 style만을 본다면 PMD는 io에 대한 close check, exception 등에 대한 정확한 handling이 되었는지 판별하게 됩니다. 대체적으로 PMD는 경험적인 rule이 만들어진 결과입니다. 나중에 보실 findbugs와는 조금 다른 code analysis를 보여줍니다.

PMD는 pmd plugin을 설치해서 사용 가능합니다. 

apply plugin : 'pmd'

pmd의 결과는 기본적으로 html format으로 나오게 됩니다. 또한 결과를 xml로도 볼수 있는데, xml결과는 후에 jenkins와 같은 CI tool에서 이용이 가능합니다. 이에 대한 설정은 다음과 같이 해주면 됩니다. 

pmdMain {
     reports {
          xml.destination = file("${pmd.reportsDir}/pmd.xml")
          html.enabled = false
          xml.enabled = true
     }
}


3. findbugs

findbugs는 정형적인 버그를 발견할 수 있는 tool입니다. findbugs를 이용하면 버그가 발생할 수 있는 상황을 확인하는 것이 가능합니다. type의 변환시에 값의 크기가 작아서 오작동을 일으킨다던지, 무한 loop를 돌 수 있는 pattern을 찾아낼 수 있는 방법들을 제공합니다. 

findbugs는 findbugs plugin을 설치해서 사용합니다. 


apply plugin: 'findbugs'


역시 findbugs도 PMD와 checkstyle과 같이 xml/html의 결과를 같이 보여주게 됩니다. report에 대한 설정은 다음과 같습니다. 

findbugsMain {
     reports {
          xml.enabled = true
          html.enabled = false
          xml.destination = file("${findbugs.reportsDir}/findbugs.xml")
     }
}


multi project에서 checkstyle, PMD, findbugs의 이용

multi project의 경우에는 위의 설정이 모두 subprojects에 들어가면 됩니다. 그렇지만, 위의 설정대로라면 조금 문제가 발생하게 됩니다. 각 subproject들의 checkstyle, PMD, findbugs에 warning이 발견되면 즉시 build가 중지되기 때문입니다. 따라서, 각 plugin들에 failure가 발생되더라도 계속해서 진행하는 option을 걸어줘야지 됩니다. 
또한, 각 static code analysis의 결과를 모아서 볼 필요가 있습니다. 각각의 결과들을 깊은 depth를 갖는 folder명에서 보는 것은 조금 힘듭니다. 따라서 root project가 report 결과를 모으는 작업을 할 필요가 존재합니다. 

output folder에 모든 report의 결과는 다음과 같이 표시하면 됩니다. 

ext {
     checkStylePath = file('config/checkstyle.xml')
     reportPath = file('output/report');
     reportPathA = reportPath.absolutePath.toString()
}

if(!ext.reportPath.exists()) { // 폴더가 없는 경우에 신규 생성
     ext.reportPath.mkdir()
}


subprojects {

     apply plugin: 'java'
     apply plugin: 'checkstyle'
     apply plugin: 'pmd'
     apply plugin: 'findbugs'

     sourceCompatibility = 1.7
     targetCompatibility = 1.7

     checkstyle {
          configFile = rootProject.ext.checkStylePath
          ignoreFailures = true
     }

     pmd {
          ignoreFailures = true
     }

     findbugs {
          ignoreFailures = true
     }

     findbugsMain {
          reports {
               xml.enabled = true
               html.enabled = false
               xml.destination = file("${rootProject.ext.reportPathA}/findbug/${project.name}.xml")
          }
     } 

     pmdMain {
          reports {
               xml.destination = file("${rootProject.ext.reportPathA}/pmd/${project. name}.xml")
               html.enabled = false
          }
     }

     checkstyleMain {
          reports {
               xml.enabled = true
               xml.destination = file("${rootProject.ext.reportPathA}/checkstyle/ ${project.name}.xml")
               // html.enabled = false
          }
     }

}


위와 같은 build script를 이용하면 다음과 같이 한개의 폴더 안에 잘 정리된 코드 분석 결과를 얻어낼 수 있습니다.


Posted by Y2K
,