Gradle project structure support
authorTim Boudreau <tboudreau@netbeans.org>
Mon, 29 Aug 2016 05:21:59 -0400
changeset 1837225e1d840480b
parent 18371 af501c43b864
child 18373 3527a32a19f0
Gradle project structure support
callgraph/pom.xml
callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java
callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java
callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java
callgraph/src/main/java/org/netbeans/lib/callgraph/io/JavaFilesIterator.java
callgraph/src/main/java/org/netbeans/lib/callgraph/javac/JavacRunner.java
callgraph/src/test/java/org/netbeans/lib/callgraph/ArgumentsTest.java
     1.1 --- a/callgraph/pom.xml	Tue Aug 09 12:01:40 2016 +0200
     1.2 +++ b/callgraph/pom.xml	Mon Aug 29 05:21:59 2016 -0400
     1.3 @@ -4,12 +4,12 @@
     1.4      <groupId>org.netbeans</groupId>
     1.5      <artifactId>callgraph</artifactId>
     1.6      <name>Callgraph Utility</name>
     1.7 -    <version>1.0</version>
     1.8 +    <version>1.1</version>
     1.9      <packaging>jar</packaging>
    1.10      <properties>
    1.11          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    1.12 -        <maven.compiler.source>1.7</maven.compiler.source>
    1.13 -        <maven.compiler.target>1.7</maven.compiler.target>
    1.14 +        <maven.compiler.source>1.8</maven.compiler.source>
    1.15 +        <maven.compiler.target>1.8</maven.compiler.target>
    1.16      </properties>
    1.17      <build>
    1.18          <plugins>
    1.19 @@ -67,14 +67,14 @@
    1.20          <dependency>
    1.21              <groupId>org.netbeans.external</groupId>
    1.22              <artifactId>nb-javac-api</artifactId>
    1.23 -            <version>RELEASE80</version>
    1.24 +            <version>RELEASE81</version>
    1.25              <type>jar</type>
    1.26          </dependency>
    1.27          <dependency>
    1.28              <groupId>org.netbeans.external</groupId>
    1.29              <artifactId>nb-javac-impl</artifactId>
    1.30              <!--<scope>provided</scope>-->
    1.31 -            <version>RELEASE80</version>
    1.32 +            <version>RELEASE81</version>
    1.33              <type>jar</type>
    1.34          </dependency>
    1.35          <dependency>
     2.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java	Tue Aug 09 12:01:40 2016 +0200
     2.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java	Mon Aug 29 05:21:59 2016 -0400
     2.3 @@ -1,4 +1,4 @@
     2.4 -/* 
     2.5 +/*
     2.6   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     2.7   *
     2.8   * Copyright (C) 1997-2015 Oracle and/or its affiliates. All rights reserved.
     2.9 @@ -41,10 +41,10 @@
    2.10   * Version 2 license, then the option applies only if the new code is
    2.11   * made subject to such option by the copyright holder.
    2.12   */
    2.13 -
    2.14  package org.netbeans.lib.callgraph;
    2.15  
    2.16  import java.io.File;
    2.17 +import java.io.IOException;
    2.18  import java.util.Collections;
    2.19  import java.util.HashSet;
    2.20  import java.util.Iterator;
    2.21 @@ -64,6 +64,8 @@
    2.22          new NoSelfReferencesCommand(),
    2.23          new ShortNamesCommand(),
    2.24          new MavenCommand(),
    2.25 +        new GradleCommand(),
    2.26 +        new IgnoreCommand(),
    2.27          new OutfileCommand(),
    2.28          new ExcludeCommand(),
    2.29          new PackageGraphFileCommand(),
    2.30 @@ -78,8 +80,10 @@
    2.31      private boolean noSelfReferences = false;
    2.32      private boolean shortNames = false;
    2.33      private boolean maven = false;
    2.34 +    private boolean gradle = false;
    2.35      private File outfile;
    2.36      private Set<String> exclude = new HashSet<>();
    2.37 +    private Set<String> ignore = new HashSet<>();
    2.38      private boolean quiet;
    2.39      private File classGraphFile;
    2.40      private File packageGraphFile;
    2.41 @@ -88,11 +92,11 @@
    2.42      private boolean disableEightBitStrings;
    2.43      private boolean reverse;
    2.44  
    2.45 -    Arguments(String... args) {
    2.46 +    Arguments(String... args) throws IOException {
    2.47          this(true, args);
    2.48      }
    2.49  
    2.50 -    Arguments(boolean abortIfWillNotOutput, String... args) {
    2.51 +    Arguments(boolean abortIfWillNotOutput, String... args) throws IOException {
    2.52          List<String> unknownArguments = new LinkedList<>();
    2.53          List<String> errors = new LinkedList<>();
    2.54          for (int i = 0; i < args.length;) {
    2.55 @@ -133,13 +137,63 @@
    2.56                  } else if (!folder.isDirectory()) {
    2.57                      errors.add("Not a folder: " + folderName);
    2.58                  } else {
    2.59 -                    this.folders.add(folder);
    2.60 +                    this.folders.add(folder.getCanonicalFile());
    2.61                  }
    2.62              }
    2.63          }
    2.64 -        if (maven) {
    2.65 +        Set<String> origFolders = folders;
    2.66 +        if (maven && gradle) {
    2.67 +            errors.add("--maven and --gradle are mutually exclusive");
    2.68 +        } else if (maven) {
    2.69              findMavenSubfolders(errors);
    2.70 +        } else if (gradle) {
    2.71 +            findGradleSubfolders(errors);
    2.72          }
    2.73 +        Set<File> toIgnore = new HashSet<>();
    2.74 +        for (String ig : ignore) {
    2.75 +            File ff = new File(ig);
    2.76 +            if (ff.exists() && ff.isDirectory()) {
    2.77 +                ff = ff.getCanonicalFile();
    2.78 +                for (File f : this.folders()) {
    2.79 +                    File f1 = f.getCanonicalFile();
    2.80 +                    if (f1.equals(ff)) {
    2.81 +                        toIgnore.add(f1);
    2.82 +                    } else {
    2.83 +                        if (f1.getPath().startsWith(ff.getPath())) {
    2.84 +                            toIgnore.add(f1);
    2.85 +                        }
    2.86 +                    }
    2.87 +                }
    2.88 +            } else {
    2.89 +                for (String u : unknownArguments) {
    2.90 +                    if (!u.startsWith("-")) {
    2.91 +                        File f = new File(u);
    2.92 +                        if (f.exists()) {
    2.93 +                            f = f.getCanonicalFile();
    2.94 +                            File maybeIgnore = new File(f, ig);
    2.95 +                            if (maybeIgnore.exists() && maybeIgnore.isDirectory()) {
    2.96 +                                maybeIgnore = maybeIgnore.getCanonicalFile();
    2.97 +                                for (File fld : this.folders()) {
    2.98 +                                    if (fld.equals(maybeIgnore)) {
    2.99 +                                        toIgnore.add(fld);
   2.100 +                                    } else if (fld.getAbsolutePath().startsWith(maybeIgnore.getAbsolutePath())) {
   2.101 +                                        toIgnore.add(fld);
   2.102 +                                    }
   2.103 +                                }
   2.104 +                            }
   2.105 +                        }
   2.106 +                    }
   2.107 +                }
   2.108 +            }
   2.109 +        }
   2.110 +        if (verbose && !toIgnore.isEmpty()) {
   2.111 +            System.err.println("Ignoring the following projects:");
   2.112 +            for (File ti : toIgnore) {
   2.113 +                System.err.println(" - " + ti.getAbsolutePath());
   2.114 +            }
   2.115 +        }
   2.116 +        this.folders.removeAll(toIgnore);
   2.117 +
   2.118          if (packageGraphFile != null) {
   2.119              File parent = packageGraphFile.getParentFile();
   2.120              if (!parent.exists() || !parent.isDirectory()) {
   2.121 @@ -196,6 +250,41 @@
   2.122          return pom.exists() && pom.isFile() && pom.canRead();
   2.123      }
   2.124  
   2.125 +    void findGradleSubfolders(List<String> errors) {
   2.126 +        Set<File> flds = new HashSet<>(this.folders);
   2.127 +        this.folders.clear();
   2.128 +        for (File f : flds) {
   2.129 +            recurseSubfoldersLookingForGradleProjects(f);
   2.130 +        }
   2.131 +        if (this.folders.isEmpty()) {
   2.132 +            errors.add("Did not find any gradle projects (looked for *.gradle and src/main/java in all subfolders of folder list)");
   2.133 +        }
   2.134 +    }
   2.135 +
   2.136 +    private void recurseSubfoldersLookingForGradleProjects(File file) {
   2.137 +        if (file.isDirectory()) {
   2.138 +            if (hasGradleFile(file)) {
   2.139 +                File sources = srcMainJavaFolder(file);
   2.140 +                if (sources != null) {
   2.141 +                    this.folders.add(sources);
   2.142 +                }
   2.143 +            }
   2.144 +            for (File child : file.listFiles()) {
   2.145 +                recurseSubfoldersLookingForGradleProjects(child);
   2.146 +            }
   2.147 +        }
   2.148 +    }
   2.149 +
   2.150 +    boolean hasGradleFile(File fld) {
   2.151 +        return fld.listFiles((File pathname) -> {
   2.152 +            return pathname.getName().endsWith(".gradle") && pathname.isFile() && pathname.canRead();
   2.153 +        }).length > 0;
   2.154 +    }
   2.155 +
   2.156 +    public boolean isGradle() {
   2.157 +        return gradle;
   2.158 +    }
   2.159 +
   2.160      private File srcMainJavaFolder(File projectFolder) {
   2.161          File src = new File(projectFolder, "src");
   2.162          File main = new File(src, "main");
   2.163 @@ -436,6 +525,24 @@
   2.164          }
   2.165      }
   2.166  
   2.167 +    private static final class GradleCommand extends Command {
   2.168 +
   2.169 +        GradleCommand() {
   2.170 +            super(CMD_GRADLE, "g", true, false);
   2.171 +        }
   2.172 +
   2.173 +        @Override
   2.174 +        protected int doParse(int i, String[] args, Arguments toSet) {
   2.175 +            toSet.gradle = true;
   2.176 +            return 1;
   2.177 +        }
   2.178 +
   2.179 +        @Override
   2.180 +        protected String help() {
   2.181 +            return "Find all gradle projects that are children of the passed folders, and scan their src/main/java subfolders";
   2.182 +        }
   2.183 +    }
   2.184 +
   2.185      private static final class OmitAbstractCommand extends Command {
   2.186  
   2.187          OmitAbstractCommand() {
   2.188 @@ -493,7 +600,7 @@
   2.189      private static final class OutfileCommand extends Command {
   2.190  
   2.191          OutfileCommand() {
   2.192 -            super(CMD_METHODGRAPH, "g", true, true);
   2.193 +            super(CMD_METHODGRAPH, "o", true, true);
   2.194          }
   2.195  
   2.196          @Override
   2.197 @@ -576,6 +683,29 @@
   2.198          }
   2.199      }
   2.200  
   2.201 +    private static final class IgnoreCommand extends Command {
   2.202 +
   2.203 +        IgnoreCommand() {
   2.204 +            super(CMD_IGNORE, "i", true, true);
   2.205 +        }
   2.206 +
   2.207 +        @Override
   2.208 +        protected int doParse(int i, String[] args, Arguments toSet) {
   2.209 +            if (args.length == i + 1) {
   2.210 +                throw new IllegalArgumentException("--exclude or -e present but no exclusion list present");
   2.211 +            }
   2.212 +            for (String s : args[i + 1].split(",")) {
   2.213 +                toSet.ignore.add(s);
   2.214 +            }
   2.215 +            return 2;
   2.216 +        }
   2.217 +
   2.218 +        @Override
   2.219 +        protected String help() {
   2.220 +            return "Comma delimited list of folders or subfolders to ignore, absolute or relative to the base directory.";
   2.221 +        }
   2.222 +    }
   2.223 +
   2.224      static final class InvalidArgumentsException extends IllegalArgumentException {
   2.225  
   2.226          private final List<String> errors;
     3.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java	Tue Aug 09 12:01:40 2016 +0200
     3.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java	Mon Aug 29 05:21:59 2016 -0400
     3.3 @@ -71,6 +71,8 @@
     3.4  import java.util.LinkedList;
     3.5  import java.util.List;
     3.6  import java.util.Set;
     3.7 +import java.util.concurrent.atomic.AtomicReference;
     3.8 +import java.util.function.Consumer;
     3.9  
    3.10  /**
    3.11   * Scans a source folder and runs javac against any Java sources present, and
    3.12 @@ -139,8 +141,10 @@
    3.13          }
    3.14          // The thing that will run javac
    3.15          JavacRunner runner = new JavacRunner(info, MergeIterator.toIterable(iterables), listener);
    3.16 +        AtomicReference<File> lastFile = new AtomicReference<>();
    3.17 +        Consumer<File> monitor = lastFile::set;
    3.18          // run javac
    3.19 -        Set<SourceElement> allElements = runner.go();
    3.20 +        Set<SourceElement> allElements = runner.go(monitor, lastFile);
    3.21  
    3.22          List<SourceElement> all = new ArrayList<>(allElements);
    3.23          // Sort, so textual output is more human-friendly
    3.24 @@ -275,7 +279,7 @@
    3.25          return args;
    3.26      }
    3.27  
    3.28 -    CallgraphControl build() {
    3.29 +    CallgraphControl build() throws IOException {
    3.30          List<String> args = toCommandLineArguments();
    3.31          // We use regular string processing so we get the argument validation
    3.32          String[] argList = args.toArray(new String[args.size()]);
     4.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java	Tue Aug 09 12:01:40 2016 +0200
     4.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java	Mon Aug 29 05:21:59 2016 -0400
     4.3 @@ -58,6 +58,8 @@
     4.4      static final String CMD_NOSELF = "noself";
     4.5      static final String CMD_SIMPLE = "simple";
     4.6      static final String CMD_MAVEN = "maven";
     4.7 +    static final String CMD_GRADLE = "gradle";
     4.8 +    static final String CMD_IGNORE = "ignore";
     4.9      static final String CMD_PACKAGEGRAPH = "packagegraph";
    4.10      static final String CMD_METHODGRAPH = "methodgraph";
    4.11      static final String CMD_CLASSGRAPH = "classgraph";
    4.12 @@ -84,8 +86,6 @@
    4.13  
    4.14      boolean isShortNames();
    4.15  
    4.16 -    Iterator<File> iterator();
    4.17 -
    4.18      File methodGraphFile();
    4.19  
    4.20      File packageGraphFile();
     5.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/io/JavaFilesIterator.java	Tue Aug 09 12:01:40 2016 +0200
     5.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/io/JavaFilesIterator.java	Mon Aug 29 05:21:59 2016 -0400
     5.3 @@ -69,15 +69,11 @@
     5.4      }
     5.5  
     5.6      public static Iterable<File> iterable(final File root) {
     5.7 -        return new Iterable<File>() {
     5.8 -
     5.9 -            @Override
    5.10 -            public Iterator<File> iterator() {
    5.11 -                try {
    5.12 -                    return new JavaFilesIterator(root);
    5.13 -                } catch (IOException ex) {
    5.14 -                    throw new IllegalStateException(root + "");
    5.15 -                }
    5.16 +        return () -> {
    5.17 +            try {
    5.18 +                return new JavaFilesIterator(root);
    5.19 +            } catch (IOException ex) {
    5.20 +                throw new IllegalStateException(root + "");
    5.21              }
    5.22          };
    5.23      }
    5.24 @@ -135,7 +131,9 @@
    5.25  
    5.26      @Override
    5.27      public boolean accept(File file) {
    5.28 -        return (file.isDirectory() && !file.getName().startsWith(".")) || (file.isFile() && file.canRead() && file.getName().endsWith(".java"));
    5.29 +        return (file.isDirectory() && !file.getName().startsWith(".")) 
    5.30 +                || ((file.isFile() && file.canRead() && file.getName().endsWith(".java") 
    5.31 +                && !file.getName().equals("package-info.java")));
    5.32      }
    5.33  
    5.34      @Override
     6.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/JavacRunner.java	Tue Aug 09 12:01:40 2016 +0200
     6.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/JavacRunner.java	Mon Aug 29 05:21:59 2016 -0400
     6.3 @@ -41,7 +41,6 @@
     6.4   * Version 2 license, then the option applies only if the new code is
     6.5   * made subject to such option by the copyright holder.
     6.6   */
     6.7 -
     6.8  package org.netbeans.lib.callgraph.javac;
     6.9  
    6.10  import org.netbeans.lib.callgraph.Listener;
    6.11 @@ -58,6 +57,8 @@
    6.12  import java.util.List;
    6.13  import java.util.Locale;
    6.14  import java.util.Set;
    6.15 +import java.util.concurrent.atomic.AtomicReference;
    6.16 +import java.util.function.Consumer;
    6.17  import javax.tools.Diagnostic;
    6.18  import javax.tools.DiagnosticListener;
    6.19  import javax.tools.JavaCompiler;
    6.20 @@ -89,19 +90,23 @@
    6.21          this.info = info;
    6.22      }
    6.23  
    6.24 -    private Iterable<? extends JavaFileObject> javaFileObjects(StandardJavaFileManager m) {
    6.25 -        return new JavaFileObjectIterable(files, m);
    6.26 +    private Iterable<? extends JavaFileObject> javaFileObjects(StandardJavaFileManager m, Consumer<File> monitor) {
    6.27 +        return new JavaFileObjectIterable(files, m, monitor);
    6.28      }
    6.29 -    
    6.30 -    /** Iterable that converts an Iterable<File> to an Iterable<JavaFileObject>
    6.31 -    */
    6.32 +
    6.33 +    /**
    6.34 +     * Iterable that converts an Iterable<File> to an Iterable<JavaFileObject>
    6.35 +     */
    6.36      private static final class JavaFileObjectIterable implements Iterable<JavaFileObject> {
    6.37 +
    6.38          private final Iterable<File> files;
    6.39          private final StandardJavaFileManager mgr;
    6.40 +        private final Consumer<File> consumer;
    6.41  
    6.42 -        public JavaFileObjectIterable(Iterable<File> files, final StandardJavaFileManager mgr) {
    6.43 +        public JavaFileObjectIterable(Iterable<File> files, final StandardJavaFileManager mgr, Consumer<File> consumer) {
    6.44              this.files = files;
    6.45              this.mgr = mgr;
    6.46 +            this.consumer = consumer;
    6.47          }
    6.48  
    6.49          @Override
    6.50 @@ -116,7 +121,9 @@
    6.51  
    6.52                  @Override
    6.53                  public JavaFileObject next() {
    6.54 -                    return mgr.getJavaFileObjects(fi.next()).iterator().next();
    6.55 +                    File f = fi.next();
    6.56 +                    consumer.accept(f);
    6.57 +                    return mgr.getJavaFileObjects(f).iterator().next();
    6.58                  }
    6.59  
    6.60                  @Override
    6.61 @@ -134,6 +141,7 @@
    6.62          options.add("-XDsave-parameter-names");   // Javac runs inside the IDE
    6.63          options.add("-XDsuppressAbortOnBadClassFile");   // When a class file cannot be read, produce an error type instead of failing with an exception
    6.64          options.add("-XDshouldStopPolicy=GENERATE");   // Parsing should not stop in phase where an error is found
    6.65 +//        options.add("-attrparseonly");
    6.66          options.add("-g:source"); // Make the compiler maintian source file info
    6.67          options.add("-g:lines"); // Make the compiler maintain line table
    6.68          options.add("-g:vars");  // Make the compiler maintain local variables table
    6.69 @@ -142,7 +150,7 @@
    6.70          return options;
    6.71      }
    6.72  
    6.73 -    public Set<SourceElement> go() throws IOException {
    6.74 +    public Set<SourceElement> go(Consumer<File> monitor, AtomicReference<File> lastFile) throws IOException {
    6.75          listener.onStart();
    6.76          try {
    6.77              JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    6.78 @@ -150,17 +158,17 @@
    6.79              StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, Locale.getDefault(),
    6.80                      Charset.forName("UTF-8"));
    6.81              fileManager.setLocation(StandardLocation.CLASS_OUTPUT, outdir);
    6.82 -            Iterable<? extends JavaFileObject> toCompile = javaFileObjects(fileManager);
    6.83 +            Iterable<? extends JavaFileObject> toCompile = javaFileObjects(fileManager, monitor);
    6.84  
    6.85              JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null,
    6.86                      fileManager, diagnostics, options(), null, toCompile);
    6.87 -            return parse(task).allElements;
    6.88 +            return parse(task, lastFile).allElements;
    6.89          } finally {
    6.90              listener.onFinish();
    6.91          }
    6.92      }
    6.93  
    6.94 -    private SourcesInfo parse(JavacTaskImpl task) throws IOException {
    6.95 +    private SourcesInfo parse(JavacTaskImpl task, AtomicReference<File> lastFile) throws IOException {
    6.96          listener.onStartActivity("Finding and parsing Java sources", -1);
    6.97          JavacTrees trees = JavacTrees.instance(task.getContext());
    6.98          List<CompilationUnitTree> units = new LinkedList<>();
    6.99 @@ -169,7 +177,11 @@
   6.100              units.add(tree);
   6.101          }
   6.102          listener.onStartActivity("Attributing Java sources", -1);
   6.103 -        task.analyze(); // Run attribution - the compiler flags we have set will have it not abort on unresolvable classes
   6.104 +        try {
   6.105 +            task.analyze(); // Run attribution - the compiler flags we have set will have it not abort on unresolvable classes
   6.106 +        } catch (Throwable err) {
   6.107 +            throw new Error("Thrown parsing " + lastFile.get(), err);
   6.108 +        }
   6.109          try {
   6.110              try {
   6.111                  listener.onStartActivity("Cataloging methods", units.size());
     7.1 --- a/callgraph/src/test/java/org/netbeans/lib/callgraph/ArgumentsTest.java	Tue Aug 09 12:01:40 2016 +0200
     7.2 +++ b/callgraph/src/test/java/org/netbeans/lib/callgraph/ArgumentsTest.java	Mon Aug 29 05:21:59 2016 -0400
     7.3 @@ -1,10 +1,9 @@
     7.4  package org.netbeans.lib.callgraph;
     7.5  
     7.6 -import org.netbeans.lib.callgraph.CallgraphControl;
     7.7 -import org.netbeans.lib.callgraph.Arguments;
     7.8 -import org.netbeans.lib.callgraph.Callgraph;
     7.9  import java.io.File;
    7.10  import java.io.IOException;
    7.11 +import java.util.ArrayList;
    7.12 +import java.util.List;
    7.13  import org.junit.Test;
    7.14  import static org.junit.Assert.*;
    7.15  
    7.16 @@ -16,8 +15,8 @@
    7.17  public class ArgumentsTest {
    7.18  
    7.19      @Test
    7.20 -    public void testBooleanSwitches() {
    7.21 -        File tmpdir = new File(System.getProperty("java.io.tmpdir"));
    7.22 +    public void testBooleanSwitches() throws IOException {
    7.23 +        File tmpdir = new File(System.getProperty("java.io.tmpdir")).getCanonicalFile();
    7.24          String tmp = tmpdir.getAbsolutePath();
    7.25  
    7.26          Arguments args = new Arguments("-s", tmp);
    7.27 @@ -31,11 +30,11 @@
    7.28          assertFalse(args.isSelfReferences());
    7.29          assertTrue(args.folders().contains(new File(tmp)));
    7.30  
    7.31 -        args = new Arguments("-n", "-g", tmp + File.separator + "out.txt", 
    7.32 +        args = new Arguments("-n", "-o", tmp + File.separator + "out.txt", 
    7.33                  "--exclude", "foo.bar,foo.baz", System.getProperty("java.io.tmpdir"));
    7.34          assertFalse(args.isShortNames());
    7.35          assertFalse(args.isSelfReferences());
    7.36 -        assertTrue(args.folders().contains(new File(tmp)));
    7.37 +        assertTrue("Looking for " + tmp + " but got " + args.folders().toString(), args.folders().contains(new File(tmp)));
    7.38          assertNotNull(args.methodGraphFile());
    7.39          assertEquals(new File(tmpdir, "out.txt"), args.methodGraphFile());
    7.40          assertFalse(args.excludePrefixes().isEmpty());
    7.41 @@ -44,7 +43,7 @@
    7.42      }
    7.43  
    7.44      @Test
    7.45 -    public void testBuilder() {
    7.46 +    public void testBuilder() throws IOException {
    7.47          File tmpdir = new File(System.getProperty("java.io.tmpdir"));
    7.48          String tmp = tmpdir.getAbsolutePath();
    7.49          CallgraphControl ctrl = Callgraph.configure().classGraphOutput(new File(tmpdir, "classes.txt"))
    7.50 @@ -54,19 +53,80 @@
    7.51      }
    7.52  
    7.53      @Test(expected = Arguments.InvalidArgumentsException.class)
    7.54 -    public void testNoArguments() {
    7.55 +    public void testNoArguments() throws IOException {
    7.56          new Arguments();
    7.57      }
    7.58  
    7.59      @Test(expected = Arguments.InvalidArgumentsException.class)
    7.60 -    public void testQuietAndNoOuput() {
    7.61 +    public void testQuietAndNoOuput() throws IOException {
    7.62          new Arguments("-q", System.getProperty("java.io.tmpdir"));
    7.63      }
    7.64  
    7.65      @Test(expected = Arguments.InvalidArgumentsException.class)
    7.66 -    public void testNonExistentFolder() {
    7.67 +    public void testNonExistentFolder() throws IOException {
    7.68          new Arguments("/" + System.currentTimeMillis());
    7.69      }
    7.70 +    
    7.71 +    @Test
    7.72 +    public void testGradleScan() throws IOException {
    7.73 +        File tmp = new File(System.getProperty("java.io.tmpdir"));
    7.74 +        File dir = new File(tmp, ArgumentsTest.class.getSimpleName() + "_" + System.currentTimeMillis());
    7.75 +        File project1 = new File(dir, "prj");
    7.76 +        File project2 = new File(dir, "prj2");
    7.77 +        File build1 = new File(project1, "a.gradle");
    7.78 +        File build2 = new File(project2, "b.gradle");
    7.79 +
    7.80 +        File src1 = new File(project1, "src");
    7.81 +        File main1 = new File(src1, "main");
    7.82 +        File java1 = new File(main1, "java");
    7.83 +
    7.84 +        File src2 = new File(project2, "src");
    7.85 +        File main2 = new File(src2, "main");
    7.86 +        File java2 = new File(main2, "java");
    7.87 +
    7.88 +        assertTrue(java1.mkdirs());
    7.89 +        assertTrue(java2.mkdirs());
    7.90 +        build1.createNewFile();
    7.91 +        build2.createNewFile();
    7.92 +        java1 = java1.getCanonicalFile();
    7.93 +        java2 = java2.getCanonicalFile();
    7.94 +        
    7.95 +        try {
    7.96 +            Arguments args = new Arguments(dir.getAbsolutePath());
    7.97 +            
    7.98 +            assertTrue(args.hasGradleFile(project1));
    7.99 +            assertTrue(args.hasGradleFile(project2));
   7.100 +            
   7.101 +            List<String> errors = new ArrayList<>();
   7.102 +            args.findGradleSubfolders(errors);
   7.103 +            assertFalse(args.folders().isEmpty());
   7.104 +            assertEquals(args.folders().toString(), 2, args.folders().size());
   7.105 +            assertTrue(errors + "", errors.isEmpty());
   7.106 +
   7.107 +            
   7.108 +            args = new Arguments("--gradle", "-s", dir.getAbsolutePath());
   7.109 +            
   7.110 +            assertTrue(args.isGradle());
   7.111 +            assertTrue(args.isShortNames());
   7.112 +            assertTrue(args.isSelfReferences());
   7.113 +            assertTrue(args.folders().contains(java1));
   7.114 +            assertTrue(args.folders().contains(java2));
   7.115 +        } finally {
   7.116 +            // Clean up
   7.117 +            assertTrue(build1.delete());
   7.118 +            assertTrue(build2.delete());
   7.119 +            assertTrue(java1.delete());
   7.120 +            assertTrue(java2.delete());
   7.121 +            assertTrue(main1.delete());
   7.122 +            assertTrue(main2.delete());
   7.123 +            assertTrue(src1.delete());
   7.124 +            assertTrue(src2.delete());
   7.125 +            assertTrue(project1.delete());
   7.126 +            assertTrue(project2.delete());
   7.127 +            assertTrue(dir.delete());
   7.128 +        }
   7.129 +        
   7.130 +    }
   7.131  
   7.132      @Test
   7.133      public void testMavenScan() throws IOException {
   7.134 @@ -89,6 +149,8 @@
   7.135          assertTrue(java2.mkdirs());
   7.136          pom1.createNewFile();
   7.137          pom2.createNewFile();
   7.138 +        java1 = java1.getCanonicalFile();
   7.139 +        java2 = java2.getCanonicalFile();
   7.140          try {
   7.141  
   7.142              Arguments args = new Arguments("--maven", "-s", dir.getAbsolutePath());