Menu

#506 Inconsistent use of project root in classpath

open
nobody
5
2006-07-27
2006-06-13
Dan Smith
No

To reproduce:

- Create a new project Proj. Set the working directory and the project
root to some directory Proj.
- Create a new file "SomeClass.java". Define it in package "pkg". Save it
in location "Proj/src/pkg".
- Compile. Compilation succeeds.
- Type "SomeClass c" in the interactions. An "Undefined class" error
occurs. The interactions classpath contains "Proj", but not "Proj/src".

I don't know if the intended behavior is to allow the source root to be a
subdirectory of the root (as is done during compilation), or to force it to
be the same (as is done in the interactions pane). But that choice should
be consistent.

Other operations that depend on a classpath (like Javadoc) should be
made consistent as well.

Discussion

  • Robert Cartwright

    Logged In: YES
    user_id=430590

    By definition, the project root is the root of the project
    source tree. So the term "source root" as used in the
    preceding post is synonymous with "project root". In the
    DrJava source code, the term "source root" is applied to
    individual files: the source root for a file is the
    directory where the file's path name is rooted (assuming the
    file is properly named).

    Using this terminology, the example that cite is a file in a
    project source tree has a source root distinct from the
    project root. Such a file will not be found when the
    interpreter tries to load it or when you try to execute

    java pkg.SomeClass

    and the directory containing "pkg" ("src" in your example)
    is not on the CLASSPATH.

    On the other hand, javac is more permissive; it will compile
    files that are placed in the wrong directory as long as it
    can find all of the files required for compilation using the
    CLASSPATH. The behavior that you describe is consistent
    with the permissive rules used by javac. Exactly the same
    thing happens using the command line interface. Try
    compiling SomeClass.java at the command line with the
    project root (the directory containing "src") as the only
    entry on the CLASSPATH. (I don't think the compiler even
    requires the file to be located in a directory called
    "pkg".) If you give the relative path name to the file
    SomeClass.java, compilation will succeed (for a trivial
    class that does not refer to any classes outside of the Java
    core libraries). Then try executing

    java pkg.SomeClass

    It will fail. Try moving to any other directory (so that
    "src" is not on the CLASSPATH) and executing the same
    command; it will still fail.

    When you execute

    java <fully-qualified-class-name>

    the JVM searches the CLASSPATH for such a class. The
    working directory is irrelevant (except for fact that "."
    may be on the CLASSPATH). On the other hand

    javac <relative-or-absolute filename>

    compiles the specified file assuming that it is properly
    located in some source tree but it does not check that the
    location is correct. The source root for the file does not
    have to be on the CLASSPATH as long as javac can find all
    referenced files in either the core libraries on classes on
    the CLASSPATH.

     
  • Mathias Ricken

    Mathias Ricken - 2006-07-27
    • priority: 9 --> 5
     
  • Dan Smith

    Dan Smith - 2006-07-28

    Logged In: YES
    user_id=1060117

    I see that you're right about javac. There is no way to force it to check that
    the files it compiles have a certain source root.

    As I understand it, there are three places where the source
    root is used in DrJava:

    1) It is used in the sourcepath passed to javac, and possibly
    the classpath (the latter only when there is no project build
    directory)

    2) It might be passed to javadoc as a sourcepath (not sure
    about this)

    3) When there is no project build directory, it is added to
    the classpath for everything that requires a classpath
    (notably, the Interactions Pane)

    Despite that, I can't seem to find any combination of
    project and single-file compilation steps in DrJava that
    will lead the compiler to complain about not finding a
    needed file. I have even tried referencing files in a
    different package in the source tree, and javac seems to
    have no trouble finding them. (I would have expected
    the incorrect classpath or sourcepath that is apparently
    passed to javac to cause trouble when dealing with
    interdependent files, but that doesn't seem to be the case.
    This is strange, because when I compile a single file on
    the command line, I have to provide a correct sourcepath
    or compilation fails. So maybe there's something I'm
    missing.)

    Javadoc doesn't seem to care what DrJava thinks the
    source root is, either.

    So if I'm right, the only way to observe that something is
    wrong -- that the convention <Project Root> ==
    <source root> is not being followed -- is when class files
    need to be located within the source tree. Since that makes
    the convention difficult to enforce, why not just abandon
    the assumption that it holds? We can do so either by
    calculating the source root(s) ourselves (an expensive
    operation), or by accepting the fact that we just don't know
    what the source root is. If we require a project build
    directory to be set (and providing one by default), rather
    than allowing class files to be created in the source tree,
    I think all dependencies on the source root will be removed.
    Most other IDEs automatically generate a build directory
    anyway, and I can't think of a compelling reason to prefer
    generating class files "in place".

    On the other hand, I can envision a lot of users wanting
    the project root to correspond to the "top" of a project,
    which in many cases (DrJava on Subversion included) does
    not correspond to the source root.

     
  • Robert Cartwright

    Logged In: YES
    user_id=430590

    I don't understand what is broken in our current
    conventions. I see no reason to force the user to create a
    separate build directory. This may be appropriate for
    production programming, but for small course projects it is
    overkill. If you simply use the command line interface, the
    source tree is the natural place to put class files.

    The compiler is willing to accept a list of input files that
    have multiple source roots regardless of whether they are on
    the classpath; it may add the inferred source roots for each
    file to the classpath for resolving external references. I
    have not tried any experiments that depend on such
    additions. On the other hand, the JVM ("java" command) only
    loads classes that are on the classpath. Our interpreter
    runs within a JVM and hence obeys the same conventions.

    In flat file mode, we infer a source root for each open
    files and add it to the classpath, which caused unexpected
    behavior in some cases before we started resetting the
    interactions pane on every compilation and unit test
    invocation. Old versions of class files in inactive
    directories (containing files that were open but
    subsequently closed) could be executed in preference to
    those that have just been compiled. The only common
    situation where we can still see this anomalous behavior is
    closing all files in flat file mode followed by opening a
    collection of source files that have already been compiled.
    The source roots for the files that were closed will still
    be on the classpath. I am seriously considering modifying
    DrJava to reset the interactions pane when all files are closed.

    For projects, we only add the project root (source root for
    the project) if there is no build directory or the build
    directory for ALL of the project source files. We do not
    individually determine the source root for each file and add
    it to the classpath. On the other hand, if auxiliary files
    (files not in the project source tree) are opened, the
    source root for each such file is added to the classpath.

    I insisted on only adding the project root or build
    directory for the files in the project source tree because
    this convention prevents projects from being set up with
    inconsistent package names. If such a project were run
    outside of DrJava, it would obviously break.

    Resetting the interactions pane only changes the classpath
    if files have been closed or project/user settings have been
    changed since the last reset. The source roots for these
    files will not be added the class path when the interactions
    pane is reset. The classpath is determined by what
    files/projects were open when the interactions pane was last
    reset plus any new files (outside of the project source
    tree) that have been opened since the reset.

     
  • Dan Smith

    Dan Smith - 2006-07-31

    Logged In: YES
    user_id=1060117

    Here is the problem with our current conventions: the program assumes that
    the root if the source tree is the project root, but the user is never told about
    this assumption. When a user doesn't follow that convention and then things
    go wrong (the Interactions pane doesn't recognize a name), the user doesn't
    know why or how to fix it. He will probably come to the conclusion that
    either projects or the Interactions pane are "broken."

    Yes, the same setup wouldn't work from the command-line. But at the
    command line, the user expects to specify all the relevant parameters to
    make things work. In DrJava, the user is used to the program just figuring
    these things out for him. The natural expectation is that if the sources are
    open and have been compiled, the classes they define can be used in the
    Interactions pane.

     

Log in to post a comment.

MongoDB Logo MongoDB