Search code examples
javaprocesscompilationprocessbuilder

javac: file not found in ProcessBuilder. Commands are separated in ProcessBuilder arguments


First of all I have searched the forum and the web for 5 hours, but couldn't find out what my problem is so I'm hoping this isn't a duplicate.

I am using the Java ProcessBuilder to compile and run a Java Project. When I type

javac /Users/Katzenmeister/Desktop/Final/src/edu/kit/informatik/citationmanager/*.java

to my Terminal on MacOSX I can compile the whole folder without any errors.

In my Program I have the following

public class CompilerController {

private static File directory;
private static String mainClass;
private static boolean compiled = false;

public static void setDirectory(File directory) {
    CompilerController.directory = directory;
}

public static void setMainClass(String mainClass) {
    CompilerController.mainClass = mainClass;
}

private static void compile() {
    ProcessBuilder pb = new ProcessBuilder("javac",directory.getAbsolutePath()+"/*.java");
    pb.inheritIO();
    try {
        pb.start();
        compiled = true;
    } catch (IOException ex) {
        System.out.println(ex);
    }
}

private static String[] getSourcePath() {

    String[] strArr = directory.toString().split("src");

    strArr[0] += "src";
    strArr[1] += mainClass;

    return strArr;
}

private static void run() {

    ProcessBuilder pb = new ProcessBuilder("java",getSourcePath()[0],getSourcePath()[1]);
    try {
        pb.start();
    } catch(IOException ex) {
        System.out.println(ex);
    }

}

public static void compileRun() {
    compile();
    if (compiled) {
        run();
    }
}
}

In my JavaFX Controller class I get the directory by using a DirectoryChooser and passing the absolute path to the CompilerController using setDirectory().

When I run my program I get the following error:

javac: file not found: /Users/Katzenmeister/Desktop/Final/src/edu/kit/informatik/citationmanager/*.java
Usage: javac <options> <source files>
use -help for a list of possible options

Which is as far as I can tell the same thing I got working on my Terminal. I have only been coding for 6 Months now so I lack knowledge, but I'm assuming this is due to the JVM Runtime Directory probably. Any help is very welcome. Here are some of the forum links that I have already checked for reference:

ProcessBuilder can't find file?!

Run a .java file using ProcessBuilder

How to run a Java program using ProcessBuilder inside the other Java program. (with -cp and -Xbootclasspath commands)

Compile and Run Java Program from another Java Program

ProcessBuilder can't find file?!

P.S: This is my first question on the forum. I tried my best to fit the etiquette, if I have done anything wrong please tell me so I can edit my question.

EDIT: I got the code to work as follows for anyone who might have a similar problem

private static void compile() {

    ArrayList<String> files = new ArrayList<>();
    files.add("javac");
    files.add("-sourcepath");
    files.add("./*");

    try {
        for (File file : directory.listFiles()) {
            if (!file.isHidden()) {
                files.add(file.getName());
            }
        }
    } catch(NullPointerException ex) {}

    pb.directory(new File(directory.getAbsolutePath()));
    pb.command(files);
    pb.inheritIO();

    try {
        synchronized (processLock) {
            Process pr = pb.start();
            int i = pr.waitFor();
        }
        compiled = true;
    } catch (IOException ex) {
        System.out.println(ex);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Solution

  • * is processed differently in the shell vs in ProcessBuilder: it is expanded before being passed to javac in the shell, but it is passed literally in the ProcessBuilder command.

    You can see this if you add echo before the command in the shell:

    echo javac /Users/whatever/*.java
    

    will print out something like:

    javac /Users/whatever/Foo.java /Users/whatever/Bar.java
    

    When you are invoking ProcessBuilder like that, you are doing the equivalent of single-quoting the path in the shell, like this:

    javac '/Users/whatever/*.java'
    

    which suppresses glob expansion, so javac receives /Users/whatever/*.java, as you can see if you add echo to the front:

    echo javac '/Users/whatever/*.java'
    

    prints

    javac /Users/whatever/*.java
    

    You need to expand the glob yourself, e.g. use new File("/your/directory").listFiles(), and pass all the files to the ProcessBuilder explicitly.