SCons: компиляция сгенерированных файлов.java не проверяет, являются ли они частью пакета

Я почти уверен, что это ошибка, и я уже отправил ее в список рассылки пользователей SCons; Я пишу, чтобы попросить возможные обходные пути, пока я жду, чтобы исправить это, или разъяснение, что это не ошибка и как я должен обойти это.

У меня есть минимальный случай для воспроизведения ошибки, хотя, с 2 файлами, SConstruct и src/Hello/World/HelloWorld.java.

Я использую SCons 3.0.0, с Python 2.7, на Windows 10.

SRC / Hello / World / HelloWorld.java:

package Hello.World;

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }

}

SConstruct:

env = Environment( tools = ['javac'] )

# java
env.Append( JAVAVERSION = '1.8' )
env.Append( JAVA_HOME = 'C:/Program Files/Java/jdk1.8.0_152' )
env.AppendENVPath( 'PATH', 'C:/Program Files/Java/jdk1.8.0_152/bin' )

##### directories
src = Dir( 'src' )
build = Dir( 'build' )
classDir = build.Dir( 'classes' )

##### functions
srcJava = src.File( 'Hello/World/HelloWorld.java' )
copiedJava = build.File( 'Hello/World/HelloWorld.java' )

builtJava = env.Command( target = copiedJava, source = srcJava,
                         action = Copy( '$TARGET', '$SOURCE' ) )

classes = env.Java( target = classDir, source = builtJava )

HelloWorld.java копируется в другой каталог сборки с помощью env.Command(), выполняющей Copy(), затем скопированный файл.java помещается в env.Java(). Сборка работает, и build/classes/Hello/World/HelloWorld.class создается правильно, но повторный запуск SCons приводит к проблеме.

C:/example> scons --debug=explain -Q
scons: building `build\classes\HelloWorld.class' because it doesn't exist
javac -d build\classes -sourcepath build\Hello\World build\Hello\World\HelloWorld.java

Узел SCons указывает на неправильное местоположение. Причина, по которой это происходит, из-за излучателя Javac.

В строке 96/97 SCons/Tool/javac.py:

if not f.is_derived():
        pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version)

Эти две строки по сути означают, что файл Java анализируется только для таких вещей, как операторы импорта, если исходный файл считается "не производным". Я моделирую это в своем примере, копируя файл, но на практике это большая проблема, если у вас есть сгенерированные файлы.java в проекте.

Что касается обходных путей, которые я рассмотрел до сих пор, попытка получить доступ к атрибуту is_derived() файла очень сложна, с множеством функций только для чтения, которые по существу проверяют, есть ли у узла компоновщики. Похоже, что Java-компоновщик не принимает файлы.class в качестве целевых аргументов, поэтому я не могу это исправить, если быть более явным.

Кто-нибудь еще сталкивался с этой проблемой? Есть ли умные обходные пути, о которых я не думал? Сейчас я просто изменяю это выражение if в Tool / javac.py на 'if True:', пока не смогу удалить структуру пакета из этой части проекта.

--- Обновить ---

Как указал bdbaddog, мое исправление "если верно:" не сработало; как только сгенерированный файл исчезнет, ​​SCons попытается проанализировать файл перед его сборкой. Ошибки следуют.

С тех пор я создал свою собственную версию Tool/javac.py, которую я назвал JavaFix.py, которая сбрасывает компоновщик Java () с немного улучшенным эмиттером. Большая часть файла одинакова, поэтому я просто опубликую обновленную функцию emitter и helper:

def findComDir(entry):
    """Return a node representing the base of the source tree.
    Assumes that the first module of the package is named 'com.'
    """
    node = entry
    while node.name != 'com':
        node = node.dir
        if ':' in node.name:
            return entry
    return node


def emit_java_classes(target, source, env):
    """Create and return lists of source java files
    and their corresponding target class files.
    """
    java_suffix = env.get('JAVASUFFIX', '.java')
    class_suffix = env.get('JAVACLASSSUFFIX', '.class')

    target[0].must_be_same(SCons.Node.FS.Dir)
    classdir = target[0]

    s = source[0].rentry().disambiguate()
    if isinstance(s, SCons.Node.FS.File):
        comDir = findComDir(s)
        sourcedir = comDir.dir.rdir()
    elif isinstance(s, SCons.Node.FS.Dir):
        sourcedir = s.rdir()
    else:
        raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__)

    slist = []
    js = _my_normcase(java_suffix)
    for entry in source:
        entry = entry.rentry().disambiguate()
        if isinstance(entry, SCons.Node.FS.File):
            slist.append(entry)
        elif isinstance(entry, SCons.Node.FS.Dir):
            result = SCons.Util.OrderedDict()
            dirnode = entry.rdir()
            def find_java_files(arg, dirpath, filenames):
                java_files = sorted([n for n in filenames
                                     if _my_normcase(n).endswith(js)])
                mydir = dirnode.Dir(dirpath)
                java_paths = [mydir.File(f) for f in java_files]
                for jp in java_paths:
                    arg[jp] = True
            for dirpath, dirnames, filenames in os.walk(dirnode.get_abspath()):
                find_java_files(result, dirpath, filenames)
            entry.walk(find_java_files, result)

            slist.extend(list(result.keys()))
        else:
            raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__)

    version = env.get('JAVAVERSION', '1.4')
    full_tlist = []
    for f in slist:
        tlist = []
        source_file_based = True
        pkg_dir = None
        if not f.is_derived():
            pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version)
            if classes:
                source_file_based = False
                if pkg_dir:
                    d = target[0].Dir(pkg_dir)
                    p = pkg_dir + os.sep
                else:
                    d = target[0]
                    p = ''
                for c in classes:
                    t = d.File(c + class_suffix)
                    t.attributes.java_classdir = classdir
                    t.attributes.java_sourcedir = sourcedir
                    t.attributes.java_classname = classname(p + c)
                    tlist.append(t)

        if source_file_based:
            base = f.name[:-len(java_suffix)]
            if pkg_dir:
                t = target[0].Dir(pkg_dir).File(base + class_suffix)
            else:
                pkg_dir = sourcedir.rel_path( f.dir ).replace('.', os.sep)
                if pkg_dir == os.sep:
                    pkg_dir = None

            if pkg_dir:
                p = pkg_dir + os.sep
            else:
                p = ''
            t = target[0].File(p + base + class_suffix)

            t.attributes.java_classdir = classdir
            t.attributes.java_sourcedir = sourcedir
            t.attributes.java_classname = classname(p + base)
            tlist.append(t)

        for t in tlist:
            t.set_specific_source([f])

        full_tlist.extend(tlist)

    return full_tlist, slist

Основные изменения происходят после линии if source_file_based:, На данный момент это только первый черновик, но он работает. Я просто опубликую комментарий, который я поместил в верхней части JavaFix.py, чтобы объяснить:

The original Tools/javac.py couldn't really handle generated
.java files, so this slightly enhanced copy provides a fix.
Simply import the module and call JavaFix.generate(env),
and the Java builder will be remade with an improved emitter
that is better at dealing with generated files.

--Improvements--
When calling env.Java( target, source ), source can now handle
generated files.
--If source is a File node, the output directory structure is
  deduced by copying the directory structure source is in,
  starting from 'com.' If 'com' is not in the directory path,
  SCons will not correctly locate the output node
--If source is a Dir node, SCons will correctly build all .java
  files in the directory tree so long as source is the directory
  containing the start of the package structure
  (e.g. source contains com, and the package structure is com.Foo)
--Functionality concerning non-generated .java files is preserved

Есть еще несколько минусов; SCons не распознает сгенерированные внутренние классы (файлы.class с символом $ в них), и он в значительной степени зависит от структуры каталогов, чтобы выяснить, куда направляются выходные файлы, но, по крайней мере, он не запутывается и не пытается восстановить файлы это не нужно все время.

Я надеюсь, что это поможет bdbaddog или тому, кто работает в SCons, исправит эту ошибку. Я признаю, что мой код немного грязный, но это должно быть понятно.

-Мэтью

0 ответов

Другие вопросы по тегам