3

I've got a folder called myModule. Inside it, there's another folder called myPackage in which my Main.class exists.

Next to myPackage I have my module-info.java which I'd like to compile. First take a look at the folders hierarchy:

--  myModule
    --  myPackage
        --  Main.class
    --  module-info.java

The problem is when I want to compile my module-info.java with the following command:

javac module-info.java

I get the following error:

package is empty or does not exist: myPackage

But, when I put Main.java inside myPackage and then compile both files with the same command:

javac module-info.java myPackage/Main.java

the error disappears. I can't understand why this should happen?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Joseph_Marzbani
  • 1,796
  • 4
  • 22
  • 36
  • Does `module-info.java` contain line `exports myPackage`? If yes, what is surprising that javac cannot find a nonexistent package? – ZhekaKozlov Jun 13 '20 at 13:55
  • The package is right there in the current directory with Main.class in it (Next to module-info.java itself) – Joseph_Marzbani Jun 13 '20 at 13:58
  • It's there but if you run `javac module-info.java`, javac only knows about module-info and nothing else. – ZhekaKozlov Jun 13 '20 at 14:03
  • So, it doesn't matter which of the *.java files inside the package gets compiled as long as it informs the javac that such a package exist. Is that right? If yes, what if I want to export a package of which I don't have source codes? – Joseph_Marzbani Jun 13 '20 at 14:08

2 Answers2

2

TL;DR — A module-info.java file is not designed to be a stand-alone thing. It is designed to be part of a multi-source file compilation unit. A unit which includes the module descriptor, plus at least one other source file.


The long-winded version

…there's another folder called myPackage in which my Main.class exists…

I'm gonna assume you meant to type Main.java and that Main.class is an oversight since…

  1. You don't mention anything about it being an already-compiled byte code file.
  2. I can't think of a reason why anybody would intentionally put a .class file in a source directory.

I'm also gonna assume — although you didn't mention it at all in your question — that you're intentionally following the convention recommended in the Project Jigsaw: Module System Quick-Start Guide

…By convention, the source code for the module is in a directory that is the name of the module…

I've established that compilation would succeed if your myModule module is just an empty module{} block. That is, if it doesn't declare that it exports myPackage. So I'm gonna also assume your module-info.java, like my experimental one, contains…

module myModule { 
    exports myPackage;
}

Keep in mind what the javac tool is designed to do

Description

The javac command reads source files that contain module, package and type declarations written in the Java programming language…

Also consider the reliable configuration goal of the JPMS…

Reliable configuration, to replace the brittle, error-prone class-path mechanism with a means for program components to declare explicit dependences upon one another

If I sign a contract to mow your lawn every Saturday but then never turn up, how reliable am I? Imagine if it were legal for javac to compile a module-info.java file for a module that claimed to export a package, but that package had no classes in it. How reliable is such a module?

If my build downloaded your hypothetical myModule artifact from Maven Central, I could have an import myPackage.* in my class only to find there's nothing in it. What use is a package with nothing in it?

The problem is when I want to compile my module-info.java with the following command:

javac module-info.java

The clue to the mystery is the fact that the above command results in the same error when the myPackage directory contains legal source code.

The error message isn't telling you that the file system directory is empty. It's telling you that the package that you claim the module exports is empty. Empty as in: The compiler doesn't know anything about source code you never pass to it as part of a compilation unit.

…the error disappears. I can't understand why this should happen?…

By executing: javac module-info.java myPackage/Main.java, you're composing a compilation unit made up of the things the module declares it exports. Which is the way the JPMS documentation specifies the system is expected to be used…

$ javac -d mods/com.greetings \
        src/com.greetings/module-info.java \
        src/com.greetings/com/greetings/Main.java

So typos notwithstanding, the cause of the error you report is pretty simple: Compiling a module that doesn't contain any source files is not only pointless, as far as the compiler is concerned it is a malformed module. Therefore, it's illegal.

deduper
  • 1,944
  • 9
  • 22
0

Okay, so today I ran into this exact issue, and I had the same problem as you: I didn't have the source code. However, I discovered that javac doesn't require you to have the actual source code, just a java source file in each exported package.

My solution was to create a powershell script that makes a copy of the file hierarchy, adds dummy java files to each directory whose name is a valid java package name, and compiles it

# Initial folder heirarchy:
# 
# --  staging
# --  myModule
#     --  myPackage
#         --  Main.class
#     --  module-info.java

# copy folder hierarchy (without files) from myModule to staging
xcopy myModule\* staging /t /e

# don't forget to copy the module-info.java file
Copy-Item myModule/module-info.java staging

# Get an array of all directories in staging
$Packages = Get-ChildItem staging -Directory -Recurse

# Iterate over those directories
:packageLoop foreach ($Package in $Packages) {
    # Each dummy file needs a package declaration at the top, so this generates the package name
    $PackageName = ''
    $PackageDir = $Package
    while ($PackageDir.FullName -ne (Get-Item staging).FullName) {
        if (-Not ($PackageDir.Name | Select-String -Pattern '^[a-z$_][a-z$_0-9]*$' -Quiet)) {
            # skips over this directory if package name is invalid
            continue packageLoop
        }

        $PackageName = "$($PackageDir.Name).$PackageName"
        $PackageDir = $PackageDir.Parent
    }

    $PackageName = $PackageName.Substring(0, $PackageName.Length - 1)

    # Creates the dummy file Foo.java
    New-Item "$($Package.FullName)/Foo.java" -ItemType File -Value "package $PackageName ; public class Foo { }" | Out-Null
}

# Compiles entire staging directory, including module-info.java
javac -d staging (Get-ChildItem staging\* -Recurse -File | Select-Object -ExpandProperty FullName)

# module-info.class can now be found at staging/module-info.class
Liam Bloom
  • 164
  • 2
  • 10