[mm-common] util/meson_aux: Replace shell scripts with Python scripts



commit 532d7a4218b43859db36b12b3a87e59ffb90a1f6
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date:   Wed Sep 25 15:02:45 2019 +0200

    util/meson_aux: Replace shell scripts with Python scripts
    
    Python scripts can be used on all operating systems where Meson
    can be used. Shell scripts are restricted to Unix-like systems.

 Makefile.am                          |  8 ++--
 meson.build                          | 47 ++++++++++++++--------
 util/meson_aux/extra-dist-cmd.py     | 39 ++++++++++++++++++
 util/meson_aux/extra-dist-cmd.sh     | 17 --------
 util/meson_aux/extra-install-cmd.py  | 43 ++++++++++++++++++++
 util/meson_aux/extra-install-cmd.sh  | 25 ------------
 util/meson_aux/libstdcxx-tag.py      | 77 ++++++++++++++++++++++++++++++++++++
 util/meson_aux/libstdcxx-tag.sh      | 45 ---------------------
 util/meson_aux/skeletonmm-tarball.py | 53 +++++++++++++++++++++++++
 util/meson_aux/skeletonmm-tarball.sh | 24 -----------
 10 files changed, 246 insertions(+), 132 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 4f7ad94..9b59a9f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -128,10 +128,10 @@ MAINTAINERCLEANFILES = $(dist_doctags_DATA)
 EXTRA_DIST = \
   meson.build \
   meson_options.txt \
-  util/meson_aux/extra-dist-cmd.sh \
-  util/meson_aux/extra-install-cmd.sh \
-  util/meson_aux/libstdcxx-tag.sh \
-  util/meson_aux/skeletonmm-tarball.sh
+  util/meson_aux/extra-dist-cmd.py \
+  util/meson_aux/extra-install-cmd.py \
+  util/meson_aux/libstdcxx-tag.py \
+  util/meson_aux/skeletonmm-tarball.py
 
 # Remote location of the GNU libstdc++ Doxygen tag file.
 libstdcxx_tag_url = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/libstdc++.tag
diff --git a/meson.build b/meson.build
index 57fe072..f61fa9e 100644
--- a/meson.build
+++ b/meson.build
@@ -2,10 +2,24 @@
 
 project('mm-common',
   version: '0.9.12',
-  meson_version: '>= 0.49.0',
+  meson_version: '>= 0.50.0', # required for python3.path()
   license: 'GPLv2+'
 )
 
+python3 = import('python').find_installation('python3')
+python_version = python3.language_version()
+python_version_req = '>= 3.5'
+if not python_version.version_compare(python_version_req)
+  error('Requires Python @0@, found @1@.'.format(python_version_req, python_version))
+endif
+
+# Use these instead of meson.source_root() and meson.build_root().
+# source_root() and build_root() are not useful, if this is a subproject.
+project_source_root = meson.current_source_dir()
+project_build_root = meson.current_build_dir()
+
+script_dir = project_source_root / 'util' / 'meson_aux'
+
 # Install directories are relative to {prefix}.
 install_prefix = get_option('prefix')
 install_bindir = get_option('bindir')
@@ -84,7 +98,8 @@ configure_file(
 
 if find_program('aclocal', required: false).found()
   meson.add_install_script(
-    'util' / 'meson_aux' / 'extra-install-cmd.sh',
+    python3.path(),
+    script_dir / 'extra-install-cmd.py',
     install_prefix / install_aclocal_macrodir
   )
 endif
@@ -201,22 +216,18 @@ foreach file : skeletonmm_basefiles
   skeletonmm_files += 'skeletonmm' / file
 endforeach
 
-# tar and either xz or gzip are required for the skeletonmm.tar.[xz|gz] file.
-tar = find_program('tar', required: true)
-xz = find_program('xz', required: false)
-if not xz.found()
-  gzip = find_program('gzip', required: true)
-endif
-
 # Create tar archive of skeletonmm for installation.
-tarball_filename = xz.found() ? 'skeletonmm.tar.xz' : 'skeletonmm.tar.gz'
+skeletonmm_tarball_script = script_dir / 'skeletonmm-tarball.py'
+tarball_filetype = run_command(python3, skeletonmm_tarball_script, 'check')
+tarball_filename = 'skeletonmm' + tarball_filetype.stdout()
 custom_target(tarball_filename,
   input: skeletonmm_files,
   output: tarball_filename,
   command: [
-    files('util' / 'meson_aux' / 'skeletonmm-tarball.sh'),
-    meson.current_source_dir(),
+    python3,
+    skeletonmm_tarball_script,
     '@OUTPUT@',
+    project_source_root,
     skeletonmm_files,
   ],
   build_by_default: true,
@@ -241,9 +252,10 @@ endif
 custom_target('libstdc++.tag',
   output: 'libstdc++.tag',
   command: [
-    files('util' / 'meson_aux' / 'libstdcxx-tag.sh'),
+    python3,
+    script_dir / 'libstdcxx-tag.py',
     download_cmd,
-    meson.current_source_dir() / 'doctags',
+    project_source_root / 'doctags',
     '@OUTPUT@',
   ],
   build_by_default: true,
@@ -255,9 +267,10 @@ custom_target('libstdc++.tag',
 if not meson.is_subproject()
   # Modify the contents of the distribution directory. (not allowed in a subproject)
   meson.add_dist_script(
-    'util' / 'meson_aux' / 'extra-dist-cmd.sh',
-    meson.current_source_dir(),
-    meson.current_build_dir(),
+    python3.path(),
+    script_dir / 'extra-dist-cmd.py',
+    project_source_root,
+    project_build_root,
   )
 endif
 
diff --git a/util/meson_aux/extra-dist-cmd.py b/util/meson_aux/extra-dist-cmd.py
new file mode 100755
index 0000000..5b04f06
--- /dev/null
+++ b/util/meson_aux/extra-dist-cmd.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+# External command, intended to be called with meson.add_dist_script() in meson.build
+
+#                        argv[1]          argv[2]
+# extra-dist-cmd.py <root_source_dir> <root_build_dir>
+
+# Meson does not preserve timestamps on distributed files.
+# But this script preserves the timestamps on libstdc++.tag.
+
+import os
+import sys
+import subprocess
+import shutil
+
+root_source_dir = sys.argv[1]
+root_build_dir = sys.argv[2]
+
+# Make a ChangeLog file for distribution.
+cmd = [
+  'git',
+  '--git-dir=' + os.path.join(root_source_dir, '.git'),
+  '--work-tree=' + root_source_dir,
+  'log',
+  '--no-merges',
+  '--date=short',
+  '--max-count=200',
+  '--pretty=tformat:%cd  %an  <%ae>%n%n  %s%n%w(0,0,2)%+b',
+]
+logfile = open(os.path.join(os.getenv('MESON_DIST_ROOT'), 'ChangeLog'), mode='w')
+result = subprocess.run(cmd, stdout=logfile)
+logfile.close()
+
+# Distribute the libstdc++.tag file in addition to the files in the local git clone.
+# shutil.copy2() copies timestamps and some other file metadata.
+shutil.copy2(os.path.join(root_build_dir, 'libstdc++.tag'),
+             os.path.join(os.getenv('MESON_DIST_ROOT'), 'doctags'))
+
+sys.exit(result.returncode)
diff --git a/util/meson_aux/extra-install-cmd.py b/util/meson_aux/extra-install-cmd.py
new file mode 100755
index 0000000..8414847
--- /dev/null
+++ b/util/meson_aux/extra-install-cmd.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+# External command, intended to be called with meson.add_install_script() in meson.build
+
+#                           argv[1]
+# extra-install-cmd.py <aclocal_macrodir>
+
+import os
+import sys
+import subprocess
+
+if not os.getenv('DESTDIR'):
+  # Inform the installer that M4 macro files installed in a directory
+  # not known to aclocal will not be picked up automatically.
+  # (Starting with Python 3.7 text=True is a more understandable equivalent to
+  # universal_newlines=True. Let's use only features in Python 3.5.)
+  result = subprocess.run(['aclocal', '--print-ac-dir'],
+                          stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
+                          universal_newlines=True)
+  acdir = result.stdout
+  aclocal_path = os.getenv('ACLOCAL_PATH')
+  # acdir and aclocal_path can be sequences of os.pathsep-separated paths.
+  # Merge them to one sequence with leading and trailing os.pathsep.
+  # os.pathsep is ':' for Linux, ';' for Windows.
+  acdirs = os.pathsep
+  if aclocal_path:
+    acdirs += aclocal_path + os.pathsep
+  if acdir:
+    acdirs += acdir + os.pathsep
+
+  if (os.pathsep + sys.argv[1] + os.pathsep) not in acdirs:
+    # f'''.....''' would require Python 3.6. Avoid it.
+    print('''\
+                NOTE
+                ----
+The mm-common Autoconf macro files have been installed in a different
+directory than the system aclocal directory. In order for the installed
+macros to be found, it may be necessary to add the mm-common include
+path to the ACLOCAL_PATH environment variable:
+  ACLOCAL_PATH="$ACLOCAL_PATH:{}"
+  export ACLOCAL_PATH'''.format(sys.argv[1])
+    )
+sys.exit(0)
diff --git a/util/meson_aux/libstdcxx-tag.py b/util/meson_aux/libstdcxx-tag.py
new file mode 100755
index 0000000..2873498
--- /dev/null
+++ b/util/meson_aux/libstdcxx-tag.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+
+# External command, intended to be called with custom_target() in meson.build
+
+#                         argv[1]         argv[2]    argv[3]
+# libstdcxx-tag.py <curl-or-wget-or-none> <srcdir> <output_path>
+
+import os
+import sys
+import subprocess
+import shutil
+
+subcommand = sys.argv[1]
+srcdir = sys.argv[2]
+output_path = sys.argv[3]
+output_dirname, output_filename = os.path.split(output_path)
+if not output_dirname:
+  output_dirname = '.'
+
+# Remote location of the GNU libstdc++ Doxygen tag file.
+libstdcxx_tag_url = 'http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/' + output_filename
+
+def curl():
+  cmd = [
+    'curl',
+    '--compressed',
+    '--connect-timeout', '300',
+    '--globoff',
+    '--location',
+    '--max-time', '3600',
+    '--remote-time',
+    '--retry', '5',
+  ]
+  if os.path.isfile(output_path):
+    # Don't download the tag file unless it's newer than the local file.
+    cmd += ['--time-cond', output_path]
+
+  cmd += [
+    '--output', output_path,
+    libstdcxx_tag_url,
+  ]
+  return subprocess.run(cmd).returncode
+
+def wget():
+  cmd = [
+    'wget',
+    '--timestamping',
+    '--no-directories',
+    '--timeout=300',
+    '--tries=5',
+    '--directory-prefix=' + output_dirname,
+      libstdcxx_tag_url,
+    ]
+  return subprocess.run(cmd).returncode
+
+def dont_download_tag_file():
+  if os.path.isfile(output_path):
+    print('Did not check status of', output_path, 'because network is disabled.')
+  elif os.path.isfile(os.path.join(srcdir, output_filename)):
+    print('Warning:', output_path, 'does not exist.')
+    print('Copying from the source directory because network is disabled.')
+    print('If you want an up-to-date copy, reconfigure with the -Duse-network=true option.')
+    # shutil.copy2() copies timestamps and some other file metadata.
+    shutil.copy2(os.path.join(srcdir, output_filename), output_path)
+  else:
+    print('Error:', output_path, 'does not exist.', file=sys.stderr)
+    print('Downloading it is not possible because network is disabled.', file=sys.stderr)
+    print('Please reconfigure with the -Duse-network=true option.', file=sys.stderr)
+    return 1
+  return 0
+
+# ----- Main -----
+if subcommand == 'curl':
+  sys.exit(curl())
+if subcommand == 'wget':
+  sys.exit(wget())
+sys.exit(dont_download_tag_file())
diff --git a/util/meson_aux/skeletonmm-tarball.py b/util/meson_aux/skeletonmm-tarball.py
new file mode 100755
index 0000000..576b522
--- /dev/null
+++ b/util/meson_aux/skeletonmm-tarball.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+
+# External command, intended to be called with run_command() or custom_target()
+# in meson.build
+
+#                              argv[1]           argv[2]       argv[3:]
+# skeletonmm-tarball.py <output_file_or_check> <source_dir> <input_files...>
+
+import os
+import sys
+import shutil
+import tarfile
+
+if sys.argv[1] == 'check':
+  # Called from run_command() during setup or configuration.
+  # Check which archive format can be used.
+  # In order from most wanted to least wanted: .tar.xz, .tar.gz, .tar
+  available_archive_formats = []
+  for af in shutil.get_archive_formats():
+    # Keep the formats in a list, skip the descriptions.
+    available_archive_formats += [af[0]]
+  if 'xztar' in available_archive_formats:
+    suffix = '.tar.xz'
+  elif 'gztar' in available_archive_formats:
+    suffix = '.tar.gz'
+  else: # Uncompressed tar format is always available.
+    suffix = '.tar'
+  print(suffix, end='') # stdout can be read in the meson.build file.
+  sys.exit(0)
+
+# Create an archive.
+output_file = sys.argv[1]
+source_dir = sys.argv[2]
+
+if output_file.endswith('.xz'):
+  mode = 'w:xz'
+elif output_file.endswith('.gz'):
+  mode = 'w:gz'
+else:
+  mode = 'w'
+
+tar_file = tarfile.open(output_file, mode=mode)
+os.chdir(source_dir) # Input filenames are relative to source_dir.
+for file in sys.argv[3:]:
+  tar_file.add(file)
+tar_file.close()
+# Errors raise exceptions. If an exception is raised, Meson+ninja will notice
+# that the command failed, despite exit(0).
+sys.exit(0)
+
+# shutil.make_archive() might be an alternative, but it only archives
+# whole directories. It's not useful, if you want to have full control
+# of which files are archived.


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]