changeset 39084:f9c6136cee3a

miscellaneousfixes; po files and gnulib-cache generation
author Dmitry Selyutin <ghostmansd@gmail.com>
date Sun, 17 Dec 2017 23:06:10 +0300
parents 23d338c99b61
children 6e26a5792894
files pygnulib.py pygnulib/config.py pygnulib/generator.py pygnulib/module.py pygnulib/vfs.py
diffstat 5 files changed, 361 insertions(+), 140 deletions(-) [+]
line wrap: on
line diff
--- a/pygnulib.py	Tue Dec 12 01:01:54 2017 +0300
+++ b/pygnulib.py	Sun Dec 17 23:06:10 2017 +0300
@@ -8,7 +8,9 @@
 import re
 import stat
 import sys
+import tempfile
 import traceback
+import subprocess as sp
 
 from pygnulib.error import CommandLineError
 from pygnulib.error import UnknownModuleError
@@ -17,7 +19,9 @@
 from pygnulib.config import Cache as CacheConfig
 
 from pygnulib.generator import CommandLine as CommandLineGenerator
+from pygnulib.generator import GnulibCache as GnulibCacheGenerator
 from pygnulib.generator import LibMakefile as LibMakefileGenerator
+from pygnulib.generator import POMakevars as POMakevarsGenerator
 
 from pygnulib.module import filelist
 from pygnulib.module import dummy_required
@@ -26,15 +30,16 @@
 
 from pygnulib.parser import CommandLine as CommandLineParser
 
-from pygnulib.vfs import backup
-from pygnulib.vfs import compare
-from pygnulib.vfs import copy
-from pygnulib.vfs import exists
-from pygnulib.vfs import hardlink
-from pygnulib.vfs import lookup
-from pygnulib.vfs import move
-from pygnulib.vfs import symlink
-from pygnulib.vfs import unlink
+from pygnulib.vfs import backup as vfs_backup
+from pygnulib.vfs import compare as vfs_compare
+from pygnulib.vfs import copy as vfs_copy
+from pygnulib.vfs import exists as vfs_exists
+from pygnulib.vfs import hardlink as vfs_hardlink
+from pygnulib.vfs import lookup as vfs_lookup
+from pygnulib.vfs import move as vfs_move
+from pygnulib.vfs import iostream as vfs_iostream
+from pygnulib.vfs import symlink as vfs_symlink
+from pygnulib.vfs import unlink as vfs_unlink
 from pygnulib.vfs import Base as BaseVFS
 from pygnulib.vfs import GnulibGit as GnulibGitVFS
 
@@ -47,6 +52,11 @@
     "unlimited",
     "unmodifiable license text",
 }
+TRANSFER_MODES = {
+    None: vfs_copy,
+    "hardlink": vfs_hardlink,
+    "symlink": vfs_symlink,
+}
 SUBSTITUTION_RULES = {
     "build-aux": "auxdir",
     "doc": "doc_base",
@@ -57,13 +67,15 @@
     "po": "po_base",
     "top": "",
 }
+TP_URL = "http://translationproject.org/latest/"
+TP_RSYNC_URI = "translationproject.org::tp/latest/"
 
 
 
 def list_hook(gnulib, *args, **kwargs):
     (_, _) = (args, kwargs)
     for module in sorted(gnulib.modules(full=False)):
-        print(module.name)
+        print(module.name, file=sys.stdout)
     return os.EX_OK
 
 
@@ -186,8 +198,6 @@
         old_files |= set(["m4/gnulib-tool.m4"])
     for (tbl_key, cfg_key) in SUBSTITUTION_RULES.items():
         table[tbl_key] = cache[cfg_key] if cfg_key else ""
-    with BaseVFS(config.root, **table) as vfs:
-        old_files = frozenset({vfs[file] for file in old_files})
     new_files = frozenset(files | set(["m4/gnulib-tool.m4"]))
 
     dry_run = options["dry_run"]
@@ -197,39 +207,38 @@
     def transfer_file(local, src_vfs, src_name, dst_vfs, dst_name):
         args = (src_vfs, src_name, dst_vfs, dst_name)
         if src_vfs is None:
-            return copy(*args)
-        table = {None: copy, "hardlink": hardlink, "symlink": symlink}
-        action = table[local_copymode if local else gnulib_copymode]
+            return vfs_copy(*args)
+        action = TRANSFER_MODES[local_copymode if local else gnulib_copymode]
         try:
             return action(*args)
         except OSError as error:
             if error.errno == errno.EXDEV:
-                return copy(*args)
+                return vfs_copy(*args)
             raise error
 
     def remove_file(project, file):
         action = ("Removing", "Remove")[dry_run]
         fmt = (action + " file {file} (backup in {file}~)")
         if not dry_run:
-            unlink(project, file, backup=True)
-        print(fmt.format(file=file))
+            vfs_unlink(project, file, backup=True)
+        print(fmt.format(file=file), file=sys.stdout)
 
     def update_file(local, src_vfs, src_name, dst_vfs, dst_name, present):
-        if not compare(src_vfs, src_name, dst_vfs, dst_name):
+        if not vfs_compare(src_vfs, src_name, dst_vfs, dst_name):
             action = (("Replacing", "Replace"), ("Updating", "Update"))[present][dry_run]
             message = ("(non-gnulib code backed up in {file}~) !!", "(backup in {file}~)")[present]
             fmt = (action + " file {file} " + message)
             if not dry_run:
-                backup(dst_vfs, dst_name)
+                vfs_backup(dst_vfs, dst_name)
                 transfer_file(local, src_vfs, src_name, dst_vfs, dst_name)
-            print(fmt.format(file=dst_name))
+            print(fmt.format(file=dst_name), file=sys.stdout)
 
     def add_file(local, src_vfs, src_name, dst_vfs, dst_name, present):
         action = ("Copying", "Copy")[dry_run]
         fmt = (action + " file {file}")
         if not dry_run:
             transfer_file(local, src_vfs, src_name, dst_vfs, dst_name)
-        print(fmt.format(file=dst_name))
+        print(fmt.format(file=dst_name), file=sys.stdout)
 
     # First the files that are in old_files, but not in new_files.
     # Then the files that are in new_files, but not in old_files.
@@ -241,14 +250,138 @@
         remove_file(project, file)
     for (present, files) in ((False, added_files), (True, kept_files)):
         for dst in sorted(files):
-            match = [override for override in overrides if dst in override]
-            override = match[0] if len(match) else gnulib
-            (vfs, src) = lookup(dst, gnulib, override, patch="patch")
-            action = update_file if exists(project, dst) else add_file
+            match = tuple(override for override in overrides if dst in override)
+            override = match[0] if match else gnulib
+            (vfs, src) = vfs_lookup(dst, gnulib, override, patch="patch")
+            action = update_file if vfs_exists(project, dst) else add_file
             action(bool(match), vfs, src, project, dst, present)
 
-    actioncmd = " ".join(CommandLineGenerator(config, explicit))
-    libMakefile = LibMakefileGenerator(config, explicit, main, False, actioncmd=actioncmd)
+    mkedits = []
+    if config.makefile_name == "Makefile.am":
+        dirname = os.path.dirname(config.source_base)
+        basename = os.path.basename(config.source_base)
+        dirname = "" if dirname == "." else (dirname + os.path.sep)
+        mkedits.append((dirname, "SUBDIRS", basename))
+    if "po_base" in explicit:
+        dirname = os.path.dirname(config.po_base)
+        basename = os.path.basename(config.po_base)
+        dirname = "" if dirname == "." else dirname
+        mkedits.append((dirname, "SUBDIRS", basename))
+    if config.tests and config.makefile_name == "Makefile.am":
+        dirname = os.path.dirname(config.tests_base)
+        basename = os.path.basename(config.tests_base)
+        dirname = "" if dirname == "." else (dirname + os.path.sep)
+        mkedits.append((dirname, "SUBDIRS", basename))
+    mkedits.append(("", "ACLOCAL_AMFLAGS", "-I {}".format(config.m4_base)))
+
+    # Find the first parent directory of m4_base that contains or will contain a Makefile.am.
+    (dir1, dir2) = (config.m4_base, "")
+    source_base_makefile = os.path.join(config.source_base, config.makefile_name)
+    tests_base_makefile = os.path.join(config.tests_base, config.makefile_name)
+    while dir1 != ".":
+        path = os.path.join(dir1, "Makefile.am")
+        if vfs_exists(project, os.path.join(config.root, dir1, "Makefile.am")) \
+        or (config.tests and path in (source_base_makefile, tests_base_makefile)):
+            break
+        dir2 = os.path.join(os.path.basename(dir1), dir2)
+        dir1 = os.path.dirname(dir1)
+    mkedits.append((dir1, "EXTRA_DIST", os.path.join(dir2, "gnulib-cache.m4")))
+
+    # Generate the contents of library makefile.
+    path = os.path.join(config.source_base, config.makefile_name)
+    with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
+        for line in LibMakefileGenerator(config, explicit, path, main, mkedits, False):
+            tmp.write(line + "\n")
+    (src, dst) = (tmp.name, path)
+    present = vfs_exists(project, dst)
+    if present:
+        added_files.add(dst)
+    action = update_file if present else add_file
+    action(False, None, src, project, dst, present)
+    os.unlink(tmp.name)
+
+    # Generate po makefile and directories.
+    url = (TP_RSYNC_URI + "gnulib/")
+    if "po_base" in explicit:
+        for file in ("Makefile.in.in", "remove-potcdate.sin"):
+            path = os.path.join("build-aux", "po", file)
+            match = tuple(override for override in overrides if file in override)
+            override = match[0] if match else gnulib
+            (vfs, src) = vfs_lookup(path, gnulib, override, patch="patch")
+            dst = os.path.join("po", file)
+            present = vfs_exists(project, dst)
+            if present:
+                added_files.add(dst)
+            action = update_file if present else add_file
+            action(bool(match), vfs, src, project, dst, present)
+        # Create po makefile parameterization, part 1.
+        with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
+            for line in POMakevarsGenerator(config):
+                tmp.write(line + "\n")
+        (src, dst) = (tmp.name, "po/Makevars")
+        present = vfs_exists(project, dst)
+        if present:
+            added_files.add(dst)
+        action = update_file if present else add_file
+        action(False, None, src, project, dst, present)
+        os.unlink(tmp.name)
+        # Create po makefile parameterization, part 2.
+        with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
+            for line in POMakevarsGenerator(config):
+                tmp.write(line + "\n")
+        (src, dst) = (tmp.name, "po/POTFILES.in")
+        present = vfs_exists(project, dst)
+        if present:
+            added_files.add(dst)
+        action = update_file if present else add_file
+        action(False, None, src, project, dst, present)
+        os.unlink(tmp.name)
+        # Fetch PO files.
+        po_root = os.path.join(project.absolute, project["po"])
+        fmt = ("{} gnulib PO files from " + TP_URL)
+        print(fmt.format("Fetching", "Fetch")[dry_run], file=sys.stdout)
+        if not dry_run:
+            # Prefer rsync over wget if it is available, since it consumes
+            # less network bandwidth, due to compression.
+            url = (TP_RSYNC_URI + "gnulib/")
+            cmdargs = ("rsync", "--delete", "--exclude", "*.s1", "-Lrtz", url, ".")
+            with sp.Popen(cmdargs, cwd=po_root, stdout=sp.PIPE, stderr=sp.PIPE) as process:
+                (stdout, stderr) = process.communicate()
+                stdout = stdout.decode("UTF-8")
+                stderr = stderr.decode("UTF-8")
+                returncode = process.returncode
+                if process.returncode == 0:
+                    raise sp.CalledProcessError(returncode, cmdargs, stdout, stderr)
+                print(stdout, file=sys.stdout)
+                print(stderr, file=sys.stderr)
+        # Create po/LINGUAS.
+        languages = set()
+        for root, _, files in os.walk(po_root):
+            languages.update(file[:-3] for file in files if file.endswith(".po"))
+        with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
+            tmp.write("# Set of available languages.\n")
+            for line in sorted(languages):
+                tmp.write(line + "\n")
+        (src, dst) = (tmp.name, "po/LINGUAS")
+        present = vfs_exists(project, dst)
+        if present:
+            added_files.add(dst)
+        action = update_file if present else add_file
+        action(False, None, src, project, dst, present)
+        os.unlink(tmp.name)
+
+    # Create m4/gnulib-cache.m4.
+    with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
+        for line in GnulibCacheGenerator(config):
+            tmp.write(line + "\n")
+    (src, dst) = (tmp.name, "m4/gnulib-cache.m4")
+    present = vfs_exists(project, dst)
+    if present:
+        added_files.add(dst)
+    action = update_file if present else add_file
+    action(False, None, src, project, dst, present)
+    os.unlink(tmp.name)
+
     return os.EX_OK
 
 
--- a/pygnulib/config.py	Tue Dec 12 01:01:54 2017 +0300
+++ b/pygnulib/config.py	Sun Dec 17 23:06:10 2017 +0300
@@ -20,7 +20,7 @@
     return _re.compile(regex, _re.S | _re.M)
 
 
-_ITERABLES = (list, tuple, set, frozenset)
+_ITERABLES = frozenset((list, tuple, set, frozenset, type({}.keys()), type({}.values())))
 
 
 
@@ -50,7 +50,7 @@
         "doc_base"          : "doc",
         "tests_base"        : "tests",
         "auxdir"            : "",
-        "lib"               : "libgnu",
+        "libname"           : "libgnu",
         "makefile_name"     : "Makefile.am",
         "macro_prefix"      : "gl",
         "po_domain"         : "",
@@ -228,14 +228,14 @@
 
 
     @property
-    def lib(self):
+    def libname(self):
         """library name; defaults to 'libgnu'"""
-        return self.__table["lib"]
+        return self.__table["libname"]
 
-    @lib.setter
-    def lib(self, value):
-        _type_assert("lib", value, str)
-        self.__table["lib"] = value if value else "libgnu"
+    @libname.setter
+    def libname(self, value):
+        _type_assert("libname", value, str)
+        self.__table["libname"] = value if value else "libgnu"
 
 
     @property
@@ -498,7 +498,7 @@
         """include guard prefix"""
         prefix = self.__table["macro_prefix"].upper()
         default = Base._TABLE["macro_prefix"].upper()
-        return "GL_{0}".format(prefix) if prefix == default else "GL"
+        return "GL" if prefix == default else "GL_{0}".format(prefix)
 
 
     @property
@@ -616,7 +616,7 @@
         "macro_prefix"      : (str, _compile(r"gl_MACRO_PREFIX\(\[(.*?)\]\)")),
         "po_domain"         : (str, _compile(r"gl_PO_DOMAIN\(\[(.*?)\]\)")),
         "witness_c_macro"   : (str, _compile(r"gl_WITNESS_C_MACRO\(\[(.*?)\]\)")),
-        "lib"               : (str, _compile(r"gl_LIB\(\[(.*?)\]\)")),
+        "libname"           : (str, _compile(r"gl_LIB\(\[(.*?)\]\)")),
         "modules"           : (list, _compile(r"gl_MODULES\(\[(.*?)\]\)")),
         "avoids"            : (list, _compile(r"gl_AVOID\(\[(.*?)\]\)")),
         "licenses"          : (str, _compile(r"gl_LGPL\(\[(.*?)\]\)")),
--- a/pygnulib/generator.py	Tue Dec 12 01:01:54 2017 +0300
+++ b/pygnulib/generator.py	Sun Dec 17 23:06:10 2017 +0300
@@ -21,7 +21,14 @@
 
 
 
-_ITERABLES = (list, tuple, set, frozenset, type({}.keys()), type({}.values()))
+
+_LGPL = {
+    _LGPLv2_LICENSE: "2",
+    _LGPLv3_LICENSE: "3",
+    _LGPL_LICENSE: "yes",
+    (_GPLv2_LICENSE | _LGPLv3_LICENSE): "3orGPLv2",
+}
+_ITERABLES = frozenset((list, tuple, set, frozenset, type({}.keys()), type({}.values())))
 
 
 
@@ -76,7 +83,7 @@
 
 
 
-class POMakefile(Generator):
+class POMakevars(Generator):
     """PO Makefile parameterization"""
     _TEMPLATE = (
         "# These options get passed to xgettext.",
@@ -156,7 +163,7 @@
         yield "# These two variables depend on the location of this directory."
         yield "subdir = {}".format(self.po_domain)
         yield "top_subdir = {}".format("/".join(".." for _ in self.po_base.split(_os.path.sep)))
-        for line in POMakefile._TEMPLATE:
+        for line in POMakevars._TEMPLATE:
             yield line
 
 
@@ -468,28 +475,85 @@
 
 
 
+class CommandLine(Generator):
+    """gnulib command-line invocation generator"""
+    _TESTS = {
+        "tests": "tests",
+        "obsolete": "obsolete",
+        "cxx_tests": "c++-tests",
+        "longrunning_tests": "longrunning-tests",
+        "privileged_tests": "privileged-tests",
+        "unportable_tests": "unportable-tests",
+    }
+
+    def __init__(self, config, explicit):
+        _type_assert("config", config, _BaseConfig)
+        _type_assert("explicit", explicit, _ITERABLES)
+        self.__config = config
+        self.__explicit = explicit
+
+
+    def __iter__(self):
+        config = self.__config
+        explicit = self.__explicit
+        yield "gnulib-tool --import"
+        for path in config.overrides:
+            yield "--local-dir={}".format(path)
+        yield "--lib={}".format(config.libname)
+        yield "--source-base={}".format(config.source_base)
+        yield "--m4-base={}".format(config.m4_base)
+        if "po_base" in explicit:
+            yield "--po-base={}".format(config.po_base)
+        yield "--doc-base={}".format(config.doc_base)
+        yield "--tests-base={}".format(config.tests_base)
+        yield "--aux-dir={}".format(config.auxdir)
+        for (key, value) in CommandLine._TESTS.items():
+            if config[key]:
+                yield "--with-{}".format(value)
+        if config.all_tests:
+            yield "--with-all-tests"
+        for module in config.avoids:
+            yield "--avoid={}".format(module)
+        if config.licenses in _LGPL:
+            lgpl = _LGPL[config.licenses]
+            yield "--lgpl={}".format(lgpl) if lgpl != "yes" else "--lgpl"
+        if config.gnumake:
+            yield "--gnu-make"
+        if "makefile_name" in explicit:
+            yield "--makefile-name={}".format(config.makefile_name)
+        yield "--{}conditional-dependencies".format("" if config.conddeps else "no-")
+        yield "--{}libtool".format("" if config.libtool else "no-")
+        yield "--macro-prefix={}".format(config.macro_prefix)
+        if "po_domain" in explicit:
+            yield "--po-domain={}".format(config.po_domain)
+        if "witness_c_macro" in explicit:
+            yield "--witness-c-macro={}".format(config.witness_c_macro)
+        if "vc_files" in explicit:
+            yield "--{}vc-files".format("" if config.vc_files else "no-")
+        for module in sorted(config.modules):
+            yield "{}".format(module)
+
+
+
 class LibMakefile(Generator):
     _LDFLAGS = _re.compile(r"^lib_LDFLAGS\s*\+\=.*?$", _re.S)
     _LIBNAME = _re.compile(r"lib_([A-Z][A-Z]*)", _re.S)
     _GNUMAKE = _re.compile(r"^if (.*?)$", _re.S)
 
 
-    def __init__(self, config, explicit, modules, for_test,
-        autoconf="autoconf", configure_ac="configure.ac", actioncmd=""):
+    def __init__(self, config, explicit, path, modules, mkedits, for_test):
         _type_assert("config", config, _BaseConfig)
         _type_assert("explicit", explicit, _ITERABLES)
+        _type_assert("path", path, str)
         _type_assert("modules", modules, _ITERABLES)
+        _type_assert("mkedits", mkedits, _ITERABLES)
         _type_assert("for_test", for_test, bool)
-        _type_assert("actioncmd", actioncmd, str)
-        _type_assert("autoconf", autoconf, str)
-        _type_assert("configure_ac", configure_ac, str)
         self.__config = config
         self.__explicit = explicit
+        self.__path = path
         self.__modules = modules
+        self.__mkedits = mkedits
         self.__for_test = for_test
-        self.__actioncmd = actioncmd
-        self.__autoconf = autoconf
-        self.__configure_ac = configure_ac
 
 
     def __iter__(self):
@@ -497,13 +561,12 @@
         config = self.__config
         explicit = self.__explicit
         for_test = self.__for_test
-        actioncmd = self.__actioncmd
         modules = self.__modules
 
         gnumake = config.gnumake
         libtool = config.libtool
         kwargs = {
-            "libname": config.lib,
+            "libname": config.libname,
             "macro_prefix": config.macro_prefix,
             "libext": "la" if libtool else "a",
             "perhaps_LT": "LT" if libtool else "",
@@ -527,6 +590,7 @@
         # that is to be preprocessed by config.status is 3070.  config.status uses
         # awk, and the HP-UX 11.00 awk fails if a line has length >= 3071;
         # similarly, the IRIX 6.5 awk fails if a line has length >= 3072.
+        actioncmd = " ".join(CommandLine(config, explicit))
         if len(actioncmd) <= 3000:
             yield "# Reproduce by: {}".format(actioncmd)
         yield ""
@@ -579,7 +643,7 @@
                 if transform_check_PROGRAMS:
                     conditional = conditional.replace("check_PROGRAMS", "noinst_PROGRAMS")
                 conditional = conditional.replace(r"${gl_include_guard_prefix}", config.include_guard_prefix)
-                unconditional = module.unconditional_automake_snippet
+                unconditional = module.unconditional_automake_snippet(config.auxdir)
                 unconditional = LibMakefile._LIBNAME.sub("{libname}_{libext}_\\1".format(**kwargs), unconditional)
                 if (conditional + unconditional).strip():
                     lines.append("## begin gnulib module {}".format(module.name))
@@ -622,22 +686,28 @@
 
         if gnumake:
             yield "# Start of GNU Make output."
+            self.__autoconf = "autoconf"
+            self.__configure_ac = "configure.ac"
             cmdargs = (self.__autoconf, "-t", "AC_SUBST:$1 = @$1@", self.__configure_ac)
-            process = _sp.Popen(cmdargs, stdout=_sp.PIPE, stderr=_sp.PIPE)
-            (stdout, stderr) = process.communicate()
-            stdout = stdout.decode("UTF-8")
-            stderr = stderr.decode("UTF-8")
-            if process.returncode == 0:
-                for line in sorted(stdout.splitlines()):
-                    yield line
-            else:
-                yield "== gnulib-tool GNU Make output failed as follows =="
-                for line in stderr.splitlines():
-                    yield "# stderr: {}".format(line)
+            with _sp.Popen(cmdargs, stdout=_sp.PIPE, stderr=_sp.PIPE) as process:
+                (stdout, stderr) = process.communicate()
+                stdout = stdout.decode("UTF-8")
+                stderr = stderr.decode("UTF-8")
+                if process.returncode == 0:
+                    for line in sorted(stdout.splitlines()):
+                        yield line
+                else:
+                    yield "== gnulib-tool GNU Make output failed as follows =="
+                    for line in stderr.splitlines():
+                        yield "# stderr: {}".format(line)
             yield "# End of GNU Make output."
         else:
             yield "# No GNU Make output."
 
+        for (directory, key, value) in self.__mkedits:
+            if key and _os.path.join(directory, "Makefile.am") == self.__path:
+                yield f"{key} += {value}"
+
         cppflags = "".join((
             " -D{}=1".format(config.witness_c_macro) if "witness_c_macro" in explicit else "",
             " -DGNULIB_STRICT_CHECKING=1" if for_test else "",
@@ -706,66 +776,64 @@
 
 
 
-class CommandLine(Generator):
-    """gnulib command-line invocation generator"""
-    _TESTS = {
-        "tests": "tests",
-        "obsolete": "obsolete",
-        "cxx_tests": "c++-tests",
-        "longrunning_tests": "longrunning-tests",
-        "privileged_tests": "privileged-tests",
-        "unportable_tests": "unportable-tests",
-    }
-    _LGPL = {
-        _LGPLv2_LICENSE: "2",
-        _LGPLv3_LICENSE: "3",
-        _LGPL_LICENSE: "yes",
-        (_GPLv2_LICENSE | _LGPLv3_LICENSE): "3orGPLv2",
-    }
-
-    def __init__(self, config, explicit):
+class GnulibCache(Generator):
+    def __init__(self, config):
         _type_assert("config", config, _BaseConfig)
-        _type_assert("explicit", explicit, _ITERABLES)
         self.__config = config
-        self.__explicit = explicit
 
 
     def __iter__(self):
+        date = _datetime.now()
         config = self.__config
-        explicit = self.__explicit
-        yield "gnulib-tool --import"
-        for path in config.overrides:
-            yield "--local-dir={}".format(path)
-        yield "--lib={}".format(config.lib)
-        yield "--source-base={}".format(config.source_base)
-        yield "--m4-base={}".format(config.m4_base)
-        if "po_base" in explicit:
-            yield "--po-base={}".format(config.po_base)
-        yield "--doc-base={}".format(config.doc_base)
-        yield "--tests-base={}".format(config.tests_base)
-        yield "--aux-dir={}".format(config.auxdir)
-        for (key, value) in CommandLine._TESTS.items():
-            if config[key]:
-                yield "--with-{}".format(value)
+        yield "## DO NOT EDIT! GENERATED AUTOMATICALLY!"
+        yield "## Process this file with automake to produce Makefile.in."
+        yield "# Copyright (C) 2002-{} Free Software Foundation, Inc.".format(date.year)
+        for line in super().__iter__():
+            yield line
+        yield "#"
+        yield "# This file represents the specification of how gnulib-tool is used."
+        yield "# It acts as a cache: It is written and read by gnulib-tool."
+        yield "# In projects that use version control, this file is meant to be put under"
+        yield "# version control, like the configure.ac and various Makefile.am files."
+        yield ""
+        yield ""
+        yield "# Specification in the form of a command-line invocation:"
+        yield "gl_LOCAL_DIR([$relative_local_gnulib_path])"
+        yield "gl_MODULES(["
+        for module in sorted(config.modules):
+            yield "  {}".format(module)
+        yield "])"
+        if config.obsolete:
+            yield "gl_WITH_OBSOLETE"
+        if config.cxx_tests:
+            yield "gl_WITH_CXX_TESTS"
+        if config.longrunning_tests:
+            yield "gl_WITH_LONGRUNNING_TESTS"
+        if config.privileged_tests:
+            yield "gl_WITH_PRIVILEGED_TESTS"
+        if config.unportable_tests:
+            yield "gl_WITH_UNPORTABLE_TESTS"
         if config.all_tests:
-            yield "--with-all-tests"
-        for module in config.avoids:
-            yield "--avoid={}".format(module)
-        if config.licenses in CommandLine._LGPL:
-            lgpl = CommandLine._LGPL[config.licenses]
-            yield "--lgpl={}".format(lgpl) if lgpl == "yes" else " --lgpl"
-        if config.gnumake:
-            yield "--gnu-make"
-        if "makefile_name" in explicit:
-            yield "--makefile-name={}".format(config.makefile_name)
-        yield "--{}conditional-dependencies".format("" if config.conddeps else "no-")
-        yield "--{}libtool".format("" if config.libtool else "no-")
-        yield "--macro-prefix={}".format(config.macro_prefix)
-        if "po_domain" in explicit:
-            yield "--po-domain={}".format(config.po_domain)
-        if "witness_c_macro" in explicit:
-            yield "--witness-c-macro={}".format(config.witness_c_macro)
-        if "vc_files" in explicit:
-            yield "--{}vc-files".format("" if config.vc_files else "no-")
-        for module in sorted(config.modules):
-            yield "{}".format(module)
+            yield "gl_WITH_ALL_TESTS"
+        yield "gl_AVOID([{}])".format(" ".join(sorted(config.avoids)))
+        yield "gl_SOURCE_BASE([{}])".format(config.source_base)
+        yield "gl_M4_BASE([{}])".format(config.m4_base)
+        yield "gl_PO_BASE([{}])".format(config.po_base)
+        yield "gl_DOC_BASE([{}])".format(config.doc_base)
+        yield "gl_TESTS_BASE([{}])".format(config.tests_base)
+        if config.tests:
+            yield "gl_WITH_TESTS"
+        yield "gl_LIB([{}])".format(config.libname)
+        if config.licenses in _LGPL:
+            lgpl = _LGPL[config.licenses]
+            yield "gl_LGPL([{}])".format(lgpl) if lgpl != "yes" else "gl_LGPL"
+        yield "gl_MAKEFILE_NAME([{}])".format(config.makefile_name)
+        if config.conddeps:
+            yield "gl_CONDITIONAL_DEPENDENCIES"
+        if config.libtool:
+            yield "gl_LIBTOOL"
+        yield "gl_MACRO_PREFIX([{}])".format(config.macro_prefix)
+        yield "gl_PO_DOMAIN([{}])".format(config.po_domain)
+        yield "gl_WITNESS_C_MACRO([{}])".format(config.witness_c_macro)
+        if config.vc_files:
+            yield "gl_VC_FILES([{}])".format(" ".join(sorted(config.vc_files)))
--- a/pygnulib/module.py	Tue Dec 12 01:01:54 2017 +0300
+++ b/pygnulib/module.py	Sun Dec 17 23:06:10 2017 +0300
@@ -16,7 +16,7 @@
 
 
 
-_ITERABLES = (list, tuple, set, frozenset)
+_ITERABLES = frozenset((list, tuple, set, frozenset, type({}.keys()), type({}.values())))
 
 
 
@@ -276,8 +276,7 @@
         self.__table["conditional_automake_snippet"] = value
 
 
-    @property
-    def unconditional_automake_snippet(self):
+    def unconditional_automake_snippet(self, auxdir):
         """Makefile.am snippet that must stay outside of Automake conditionals"""
         if "unconditional_automake_snippet" in self.__table:
             return self.__table["unconditional_automake_snippet"]
@@ -314,9 +313,6 @@
         mentioned_files = tuple(_collections.OrderedDict.fromkeys(mentioned_files))
         lib_files = tuple(file[len("lib/"):] for file in all_files if file.startswith("lib/"))
         extra_files = tuple(file for file in lib_files if file not in mentioned_files)
-        import sys
-        if self.name == "getprogname":
-            print(mentioned_files, file=sys.stderr)
         if extra_files:
             result += ("EXTRA_DIST += {}".format(" ".join(sorted(extra_files))) + "\n")
 
@@ -337,8 +333,9 @@
                 result += ("EXTRA_lib_SOURCES += {}".format(" ".join(sorted(extra_files))) + "\n")
 
         # Synthesize an EXTRA_DIST augmentation also for the files in build-aux/.
+        prefix = _os.path.join("$(top_srcdir)", auxdir)
         buildaux_files = (file for file in all_files if file.startswith("build-aux/"))
-        buildaux_files = tuple("$(top_srcdir)/{auxdir}/" + file[len("build-aux/"):] for file in buildaux_files)
+        buildaux_files = tuple(_os.path.join(prefix, file[len("build-aux/"):]) for file in buildaux_files)
         if buildaux_files:
             result += ("EXTRA_DIST += {}".format(" ".join(sorted(buildaux_files))) + "\n")
         self.__table["unconditional_automake_snippet"] = result
@@ -650,4 +647,4 @@
     files.add("m4/gnulib-common.m4")
     if ac_version == 2.59:
         files.add("m4/onceonly.m4")
-    return frozenset(files)
+    return files
--- a/pygnulib/vfs.py	Tue Dec 12 01:01:54 2017 +0300
+++ b/pygnulib/vfs.py	Sun Dec 17 23:06:10 2017 +0300
@@ -131,6 +131,7 @@
 def mkdir(root, name):
     """Create a leaf directory and all intermediate ones recursively."""
     root = Base(".") if root is None else root
+    path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
     _os.makedirs(root[name], exist_ok=True)
 
 
@@ -150,8 +151,11 @@
     """Compare the given files; return True if files contain the same data."""
     lhs_root = Base(".") if lhs_root is None else lhs_root
     rhs_root = Base(".") if rhs_root is None else rhs_root
-    lhs_path = _os.path.join(lhs_root.absolute, lhs_root[lhs_name])
-    rhs_path = _os.path.join(rhs_root.absolute, rhs_root[rhs_name])
+    (lhs_path, rhs_path) = (lhs_name, rhs_name)
+    if not _os.path.isabs(lhs_name):
+        lhs_path = _os.path.join(lhs_root.absolute, lhs_root[lhs_name])
+    if not _os.path.isabs(rhs_name):
+        rhs_path = _os.path.join(rhs_root.absolute, rhs_root[rhs_name])
     return _filecmp.cmp(lhs_path, rhs_path, shallow=False)
 
 
@@ -165,8 +169,11 @@
     src_root = Base(".") if src_root is None else src_root
     dst_root = Base(".") if dst_root is None else dst_root
     mkdir(dst_root, _os.path.dirname(dst_name))
-    src_path = _os.path.join(src_root.absolute, src_root[src_name])
-    dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+    (src_path, dst_path) = (src_name, dst_name)
+    if not _os.path.isabs(src_name):
+        src_path = _os.path.join(src_root.absolute, src_root[src_name])
+    if not _os.path.isabs(dst_name):
+        dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
     with _codecs.open(src_path, "rb") as istream:
         with _codecs.open(dst_path, "wb") as ostream:
             while 1:
@@ -179,7 +186,7 @@
 def exists(root, name):
     """Check whether the given file exists."""
     root = Base(".") if root is None else root
-    path = _os.path.join(root.absolute, root[name])
+    path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
     return _os.path.exists(path)
 
 
@@ -193,8 +200,11 @@
     dst_root = Base(".") if dst_root is None else dst_root
     mkdir(src_root, _os.path.dirname(src_name))
     mkdir(dst_root, _os.path.dirname(dst_name))
-    src_path = _os.path.join(src_root.absolute, src_root[src_name])
-    dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+    (src_path, dst_path) = (src_name, dst_name)
+    if not _os.path.isabs(src_name):
+        src_path = _os.path.join(src_root.absolute, src_root[src_name])
+    if not _os.path.isabs(dst_name):
+        dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
     _os.link(src_path, dst_path)
 
 
@@ -207,16 +217,26 @@
     src_root = Base(".") if src_root is None else src_root
     dst_root = Base(".") if dst_root is None else dst_root
     mkdir(dst_root, _os.path.dirname(dst_name))
-    src_path = _os.path.join(src_root.absolute, src_root[src_name])
-    dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+    (src_path, dst_path) = (src_name, dst_name)
+    if not _os.path.isabs(src_name):
+        src_path = _os.path.join(src_root.absolute, src_root[src_name])
+    if not _os.path.isabs(dst_name):
+        dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
     _os.rename(src_path, dst_path)
 
 
+def iostream(root, name, mode="r", encoding=None):
+    """Open file and return a stream. Raise IOError upon failure."""
+    root = Base(".") if root is None else root
+    path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
+    return _codecs.open(path, mode, encoding)
+
+
 def readlink(root, name):
     """Obtain the path to which the symbolic link points."""
     root = Base(".") if root is None else root
     mkdir(root, _os.path.dirname(name))
-    path = _os.path.join(root.absolute, root[name])
+    path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
     return _os.readlink(path)
 
 
@@ -230,8 +250,11 @@
     dst_root = Base(".") if dst_root is None else dst_root
     mkdir(dst_root, _os.path.dirname(dst_name))
     if not relative:
-        src_path = _os.path.join(src_root.absolute, src_root[src_name])
-        dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+        (src_path, dst_path) = (src_name, dst_name)
+        if not _os.path.isabs(src_name):
+            src_path = _os.path.join(src_root.absolute, src_root[src_name])
+        if not _os.path.isabs(dst_name):
+            dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
     else:
         src_path = _os.path.join(src_root.relative, src_root[src_name])
         dst_path = _os.path.join(dst_root.relative, dst_root[dst_name])
@@ -246,7 +269,7 @@
     """Unlink a file, backing it up if necessary."""
     root = Base(".") if root is None else root
     mkdir(root, _os.path.dirname(name))
-    path = _os.path.join(root.absolute, root[name])
+    path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
     _os.unlink(path)