Search code examples
javamavenmaven-3

Maven Dependency Plugin - Change Output Format of Dependency Tree


I use Maven 3.3.9 and the Maven Dependency Plugin version 2.4 to generate a tree of module dependencies in GraphML format. This file shall be imported to a tool such as yed to generate a dependency chart.

I use the following command for testing:

mvn dependency:tree -DoutputType=graphml -DoutputFile=dependency.graphml

The problem I have, is that every node within the file has way too much information for my needs. That makes my charts pretty unreadable.

What I get as output (this is an example):

org.apache.maven.plugins:maven-dependency-plugin:maven-plugin:2.0-alpha-5-SNAPSHOT

What I would like to have (this is an example):

maven-dependency-plugin

How can I modify the output format to meet my needs?


Solution

  • It would be helpful to get the current & desired output and to know the purpose (if possible) because maven has many features and we could prevent you from "re-inventing the wheel" and save your time.

    I've read the docs, it seems they are not exposing interface to exclude/include part of the dependency, best solution so far is using grep

    $ mvn -v
    Apache Maven 3.3.9
    

    Outputtype dot is more friendly to for grepping

    $ mvn dependency:tree -DoutputType=dot
    [INFO] Scanning for projects...
    [INFO]                                                                         
    [INFO] ------------------------------------------------------------------------
    [INFO] Building test 1.0
    [INFO] ------------------------------------------------------------------------
    [INFO] 
    [INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ test ---
    [INFO] digraph "com.a:test:jar:1.0" { 
    [INFO]  "com.a:test:jar:1.0" -> "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" ; 
    [INFO]  "com.a:test:jar:1.0" -> "com.google.code.gson:gson:jar:2.8.2:compile" ; 
    [INFO]  "com.a:test:jar:1.0" -> "info.picocli:picocli:jar:2.3.0:compile" ; 
    [INFO]  "com.a:test:jar:1.0" -> "log4j:log4j:jar:1.2.17:compile" ; 
    [INFO]  "com.a:test:jar:1.0" -> "org.xerial:sqlite-jdbc:jar:3.21.0:compile" ; 
    [INFO]  "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" -> "org.apache.httpcomponents:httpcore:jar:4.4.9:compile" ; 
    [INFO]  "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" -> "commons-logging:commons-logging:jar:1.2:compile" ; 
    [INFO]  "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" -> "commons-codec:commons-codec:jar:1.10:compile" ; 
    [INFO]  } 
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 1.215 s
    [INFO] Finished at: 2018-03-27T17:58:31+03:00
    [INFO] Final Memory: 14M/303M
    [INFO] ------------------------------------------------------------------------
    

    first grep all lines with >

    $ mvn dependency:tree -DoutputType=dot | grep \>
    [INFO]  "com.a:test:jar:1.0" -> "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" ; 
    [INFO]  "com.a:test:jar:1.0" -> "com.google.code.gson:gson:jar:2.8.2:compile" ; 
    [INFO]  "com.a:test:jar:1.0" -> "info.picocli:picocli:jar:2.3.0:compile" ; 
    [INFO]  "com.a:test:jar:1.0" -> "log4j:log4j:jar:1.2.17:compile" ; 
    [INFO]  "com.a:test:jar:1.0" -> "org.xerial:sqlite-jdbc:jar:3.21.0:compile" ; 
    [INFO]  "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" -> "org.apache.httpcomponents:httpcore:jar:4.4.9:compile" ; 
    [INFO]  "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" -> "commons-logging:commons-logging:jar:1.2:compile" ; 
    [INFO]  "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" -> "commons-codec:commons-codec:jar:1.10:compile" ; 
    

    Get the strings after >

    $ mvn dependency:tree -DoutputType=dot | grep \> | cut -d\> -f2
     "org.apache.httpcomponents:httpclient:jar:4.5.5:compile" ; 
     "com.google.code.gson:gson:jar:2.8.2:compile" ; 
     "info.picocli:picocli:jar:2.3.0:compile" ; 
     "log4j:log4j:jar:1.2.17:compile" ; 
     "org.xerial:sqlite-jdbc:jar:3.21.0:compile" ; 
     "org.apache.httpcomponents:httpcore:jar:4.4.9:compile" ; 
     "commons-logging:commons-logging:jar:1.2:compile" ; 
     "commons-codec:commons-codec:jar:1.10:compile" ; 
    

    split the string by colon, get the second match

    $ mvn dependency:tree -DoutputType=dot | grep \> | cut -d\> -f2 | cut -d: -f2
    httpclient
    gson
    picocli
    log4j
    sqlite-jdbc
    httpcore
    commons-logging
    commons-codec
    

    here you go, list of artifacts

    Update:

    pulling lines between ":tree" and "BUILD SUCCESS"

    $ mvn dependency:tree | awk '/:tree/,/BUILD SUCCESS/'
    [INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ test ---
    [INFO] com.a:test:jar:1.0
    [INFO] +- org.apache.httpcomponents:httpclient:jar:4.5.5:compile
    [INFO] |  +- org.apache.httpcomponents:httpcore:jar:4.4.9:compile
    [INFO] |  +- commons-logging:commons-logging:jar:1.2:compile
    [INFO] |  \- commons-codec:commons-codec:jar:1.10:compile
    [INFO] +- com.google.code.gson:gson:jar:2.8.2:compile
    [INFO] +- info.picocli:picocli:jar:2.3.0:compile
    [INFO] +- log4j:log4j:jar:1.2.17:compile
    [INFO] \- org.xerial:sqlite-jdbc:jar:3.21.0:compile
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    

    removing two lines from top (using awk) and bottom (using head)

    $ mvn dependency:tree | awk '/:tree/,/BUILD SUCCESS/' | awk 'NR > 2 { print }' | head -n -2
    [INFO] +- org.apache.httpcomponents:httpclient:jar:4.5.5:compile
    [INFO] |  +- org.apache.httpcomponents:httpcore:jar:4.4.9:compile
    [INFO] |  +- commons-logging:commons-logging:jar:1.2:compile
    [INFO] |  \- commons-codec:commons-codec:jar:1.10:compile
    [INFO] +- com.google.code.gson:gson:jar:2.8.2:compile
    [INFO] +- info.picocli:picocli:jar:2.3.0:compile
    [INFO] +- log4j:log4j:jar:1.2.17:compile
    [INFO] \- org.xerial:sqlite-jdbc:jar:3.21.0:compile
    

    pulling relevant lines

    $ mvn dependency:tree | awk '/:tree/,/BUILD SUCCESS/' | awk 'NR > 2 { print }' | head -n -2 | grep -o -P '.*(?<=:).*(?=:jar)'
    [INFO] +- org.apache.httpcomponents:httpclient
    [INFO] |  +- org.apache.httpcomponents:httpcore
    [INFO] |  +- commons-logging:commons-logging
    [INFO] |  \- commons-codec:commons-codec
    [INFO] +- com.google.code.gson:gson
    [INFO] +- info.picocli:picocli
    [INFO] +- log4j:log4j
    [INFO] \- org.xerial:sqlite-jdbc
    

    Removing groupId by removing string between - (dash & space) and : (colon) using sed -e 's/\(- \).*\(:\)/\1\2/'

    $ mvn dependency:tree | awk '/:tree/,/BUILD SUCCESS/' | awk 'NR > 2 { print }' | head -n -2 | grep -o -P '.*(?<=:).*(?=:jar)' | sed -e 's/\(- \).*\(:\)/\1\2/'
    [INFO] +- :httpclient
    [INFO] |  +- :httpcore
    [INFO] |  +- :commons-logging
    [INFO] |  \- :commons-codec
    [INFO] +- :gson
    [INFO] +- :picocli
    [INFO] +- :log4j
    [INFO] \- :sqlite-jdbc
    

    Removing unneccery colon using tr

    $ mvn dependency:tree | awk '/:tree/,/BUILD SUCCESS/' | awk 'NR > 2 { print }' | head -n -2 | grep -o -P '.*(?<=:).*(?=:jar)' | sed -e 's/\(- \).*\(:\)/\1\2/' | tr -d :
    [INFO] +- httpclient
    [INFO] |  +- httpcore
    [INFO] |  +- commons-logging
    [INFO] |  \- commons-codec
    [INFO] +- gson
    [INFO] +- picocli
    [INFO] +- log4j
    [INFO] \- sqlite-jdbc
    

    Update 2:

    Although you completely changed your question, here is a fancy one-liner answer:

    mvn dependency:tree -DoutputType=graphml -DoutputFile=dependency.graphml && python -c "exec(\"from bs4 import BeautifulSoup;bs = BeautifulSoup(open('dependency.graphml'), 'xml')\\nfor e in bs.find_all('NodeLabel'):    e.string = e.string.split(':')[1]\\nprint(bs.prettify())\")" > dependency_fixed.graphml
    

    Generate the dependencies tree w/

    mvn dependency:tree -DoutputType=graphml -DoutputFile=dependency.graphml

    after it completes (that's why the &&) it will execute a python script

    from bs4 import BeautifulSoup
    bs = BeautifulSoup(open('dependency.graphml'), 'xml')
    for e in bs.find_all('NodeLabel'):
        e.string = e.string.split(':')[1]
    print(bs.prettify())  # print(bs) will print the minified version
    

    which iterates over NodeLabel elements, replacing values with the second element of the split by colon result and saving the out into file with > dependency_fixed.graphml