1#!/usr/bin/env python3
2
3# External command, intended to be called with run_command(), custom_target(),
4# meson.add_install_script() and meson.add_dist_script().
5
6#                        argv[1]   argv[2:]
7# generate-binding.py <subcommand> <xxx>...
8
9import os
10import sys
11import subprocess
12from pathlib import Path
13import shutil
14
15subcommand = sys.argv[1]
16
17# Invoked from custom_target() in meson.build.
18def generate_wrap_init():
19  #    argv[2]       argv[3]      argv[4]    argv[5:]
20  # <gmmproc_dir> <output_file> <namespace> <hg_files>...
21
22  # <gmmproc_dir> is an absolute path in glibmm's installation directory.
23  # <output_file> is a relative or absolute path in the build directory.
24  # <hg_files> are relative or absolute paths in the source directory.
25  gmmproc_dir = sys.argv[2]
26  output_file = sys.argv[3]
27  parent_dir = os.path.basename(os.path.dirname(output_file))
28  namespace = sys.argv[4]
29  cmd = [
30    'perl',
31    '--',
32    os.path.join(gmmproc_dir, 'generate_wrap_init.pl'),
33    '--namespace=' + namespace,
34    '--parent_dir=' + parent_dir,
35  ] + sys.argv[5:]
36  with open(output_file, mode='w') as output_file_obj:
37    return subprocess.run(cmd, stdout=output_file_obj).returncode
38
39# Invoked from custom_target() in meson.build.
40def gmmproc():
41  #    argv[2]       argv[3]        argv[4]     argv[5]   argv[6:]
42  # <gmmproc_dir> <output_file> <basefilename> <src_dir> <m4_dirs>...
43
44  # <gmmproc_dir> is an absolute path in glibmm's installation directory.
45  # <output_file> is a relative or absolute path in the build directory.
46  # <src_dir> is an absolute path in the source directory.
47  # <m4_dirs> are absolute paths in the source directory.
48  gmmproc_dir = sys.argv[2]
49  output_file = sys.argv[3]
50  output_dir = os.path.dirname(output_file)
51  basefilename = sys.argv[4] # name without filetype
52  src_dir = sys.argv[5]
53
54  include_m4_dirs = []
55  for dir in sys.argv[6:]:
56    include_m4_dirs += ['-I', dir]
57
58  # Create the private/ directory, if it does not exist.
59  os.makedirs(os.path.join(output_dir, 'private'), exist_ok=True)
60
61  # gmmproc generates output_dir/basefilename.cc, output_dir/basefilename.h
62  # and output_dir/private/{basefilename}_p.h
63  cmd = [
64    'perl',
65    '-I' + os.path.join(gmmproc_dir, 'pm'),
66    '--',
67    os.path.join(gmmproc_dir, 'gmmproc'),
68  ] + include_m4_dirs + [
69    '--defs',
70    src_dir,
71    basefilename,
72    src_dir,
73    output_dir,
74  ]
75  result = subprocess.run(cmd)
76  if result.returncode:
77    return result.returncode
78
79  # gmmproc does not update the timestamps of output files that have not changed.
80  # That's by design, to avoid unnecessary recompilations.
81  # The updated timestamp of output_file shows meson that this custom_target()
82  # has been updated.
83  Path(output_file).touch(exist_ok=True)
84  return 0
85
86# Invoked from meson.add_install_script().
87def install_built_h_files():
88  #    argv[2]       argv[3]          argv[4:]
89  # <built_h_dir> <install_subdir> <basefilenames>...
90
91  # <built_h_dir> is an absolute path in the build directory or source directory.
92  # <install_subdir> is an installation directory, relative to {prefix}.
93  built_h_dir = sys.argv[2]
94  install_dir = os.path.join(os.getenv('MESON_INSTALL_DESTDIR_PREFIX'), sys.argv[3])
95
96  # Create the installation directory, if it does not exist.
97  os.makedirs(os.path.join(install_dir, 'private'), exist_ok=True)
98
99  for file in sys.argv[4:]:
100    path_h = os.path.join(built_h_dir, file+'.h')
101    print('Installing ', path_h, ' to ', install_dir)
102    # shutil.copy2() copies timestamps and some other file metadata.
103    shutil.copy2(path_h, install_dir)
104
105    path_h = os.path.join(built_h_dir, 'private', file+'_p.h')
106    install_priv_dir = os.path.join(install_dir, 'private')
107    print('Installing ', path_h, ' to ', install_priv_dir)
108    shutil.copy2(path_h, install_priv_dir)
109  return 0
110
111# Invoked from meson.add_dist_script().
112def dist_built_files():
113  #     argv[2]        argv[3]     argv[4:]
114  # <built_h_cc_dir> <dist_dir> <basefilenames>...
115
116  # <built_h_cc_dir> is an absolute path in the build directory or source directory.
117  # <dist_dir> is a distribution directory, relative to MESON_DIST_ROOT.
118  built_h_cc_dir = sys.argv[2]
119  dist_dir = os.path.join(os.getenv('MESON_DIST_ROOT'), sys.argv[3])
120
121  # Create the distribution directory, if it does not exist.
122  os.makedirs(os.path.join(dist_dir, 'private'), exist_ok=True)
123
124  # Distribute wrap_init.cc.
125  # shutil.copy() does not copy timestamps.
126  shutil.copy(os.path.join(built_h_cc_dir, 'wrap_init.cc'), dist_dir)
127
128  # Distribute .h/.cc/_p.h files built from .hg/.ccg files.
129  for file in sys.argv[4:]:
130    shutil.copy(os.path.join(built_h_cc_dir, file+'.h'), dist_dir)
131    shutil.copy(os.path.join(built_h_cc_dir, file+'.cc'), dist_dir)
132    shutil.copy(os.path.join(built_h_cc_dir, 'private', file+'_p.h'),
133                os.path.join(dist_dir, 'private'))
134  return 0
135
136# Invoked from run_command() in meson.build.
137def copy_built_files():
138  #  argv[2]    argv[3]    argv[4:]
139  # <from_dir> <to_dir> <basefilenames>...
140
141  # <from_dir> is an absolute or relative path of the directory to copy from.
142  # <to_dir> is an absolute or relative path of the directory to copy to.
143  from_dir = sys.argv[2]
144  to_dir = sys.argv[3]
145
146  # Create the destination directory, if it does not exist.
147  os.makedirs(os.path.join(to_dir, 'private'), exist_ok=True)
148
149  # Copy some built files if they exist in from_dir, but not in the destination
150  # directory, or if they are not up to date in the destination directory.
151  # (The term "source directory" is avoided here, because from_dir might not
152  # be what Meson calls a source directory as opposed to a build directory.)
153
154  # Copy wrap_init.cc.
155  from_file = os.path.join(from_dir, 'wrap_init.cc')
156  to_file = os.path.join(to_dir, 'wrap_init.cc')
157  if os.path.isfile(from_file) and ((not os.path.isfile(to_file))
158     or (os.stat(from_file).st_mtime > os.stat(to_file).st_mtime)):
159    shutil.copy(from_file, to_file)
160
161  # Copy .h/.cc/_p.h files built from .hg/.ccg files.
162  for basefile in sys.argv[4:]:
163    for file in [basefile+'.h', basefile+'.cc', os.path.join('private', basefile+'_p.h')]:
164      from_file = os.path.join(from_dir, file)
165      to_file = os.path.join(to_dir, file)
166      if os.path.isfile(from_file) and ((not os.path.isfile(to_file))
167         or (os.stat(from_file).st_mtime > os.stat(to_file).st_mtime)):
168        shutil.copy(from_file, to_file)
169  return 0
170
171# ----- Main -----
172if subcommand == 'generate_wrap_init':
173  sys.exit(generate_wrap_init())
174if subcommand == 'gmmproc':
175  sys.exit(gmmproc())
176if subcommand == 'install_built_h_files':
177  sys.exit(install_built_h_files())
178if subcommand == 'dist_built_files':
179  sys.exit(dist_built_files())
180if subcommand == 'copy_built_files':
181  sys.exit(copy_built_files())
182print(sys.argv[0], ': illegal subcommand,', subcommand)
183sys.exit(1)
184