chainlib-man.py (7382B)
1 #!/usr/bin/python3 2 3 import logging 4 import os 5 import sys 6 import argparse 7 import tempfile 8 import shutil 9 10 from hexathon import strip_0x, add_0x 11 12 from chainlib.cli.man import ( 13 EnvDocGenerator, 14 DocGenerator, 15 apply_groff, 16 ) 17 from chainlib.cli.arg import ArgFlag 18 #from chainlib.cli.base import ( 19 # argflag_std_base, 20 # flag_names, 21 # ) 22 from chainlib.cli.arg import ArgumentParser as ChainlibArgumentParser 23 from chainlib.cli.config import Config 24 25 26 logging.basicConfig(level=logging.WARNING) 27 logg = logging.getLogger() 28 29 30 configuration_description = """ 31 .SH CONFIGURATION 32 33 All configuration settings may be overriden both by environment variables, or by overriding settings with the contents of ini-files in the directory defined by the \\fB-c\\fP option. 34 35 The active configuration, with values assigned from environment and arguments, can be output using the \\fB--dumpconfig\\fP \\fIformat\\fP option. Note that entries having keys prefixed with underscore (e.g. _SEQ) are not actual configuration settings, and thus cannot be overridden with environment variables. 36 37 To refer to a configuration setting by environment variables, the \\fIsection\\fP and \\fIkey\\fP are concatenated together with an underscore, and transformed to upper-case. For example, the configuration variable \\fIFOO_BAZ_BAR\\fP refers to an ini-file entry as follows: 38 39 .EX 40 [foo] 41 bar_baz = xyzzy 42 .EE 43 44 In the \\fBENVIRONMENT\\fP section below, the relevant configuration settings for this tool is listed along with a short description of its meaning. 45 46 Some configuration settings may also be overriden by command line options. Also note that the use of the \\fB-n\\fP and \\fB--env-prefix\\fP options affect how environment and configuration is read. The effects of options on how configuration settings are affective is described in the respective \\fBOPTIONS\\fP section. 47 48 """ 49 50 seealso_description = """ 51 .SH SEE ALSO 52 53 .BP 54 confini-dump(1), eth-keyfile(1) 55 56 """ 57 58 legal_description = """ 59 .SH LICENSE 60 61 This documentation and its source is licensed under the Creative Commons Attribution-Sharealike 4.0 International license. 62 63 The source code of the tool this documentation describes is licensed under the GNU General Public License 3.0. 64 65 .SH COPYRIGHT 66 67 Louis Holbrook <dev@holbrook.no> (https://holbrook.no) 68 PGP: 59A844A484AC11253D3A3E9DCDCBD24DD1D0E001 69 70 """ 71 72 source_description = """ 73 74 .SH SOURCE CODE 75 76 https://git.defalsify.org 77 78 """ 79 80 argflag = ArgFlag() 81 82 argparser = argparse.ArgumentParser() 83 argparser.add_argument('-b', default=add_0x(hex(argflag.get('std_base'))), help='argument flag bitmask') 84 argparser.add_argument('-c', help='config override directory') 85 argparser.add_argument('-n', required=True, help='tool name to use for man filename') 86 argparser.add_argument('-d', default='.', help='output directory') 87 argparser.add_argument('-v', action='store_true', help='turn on debug logging') 88 #argparser.add_argument('--overrides-dir', dest='overrides_dir', help='load options description override from file') 89 argparser.add_argument('--overrides-env-dir', dest='overrides_env_dir', help='load envionment description override config from directory') 90 argparser.add_argument('--overrides-config-file', dest='overrides_config_file', help='load configuration text from file') 91 argparser.add_argument('source_dir', help='directory containing sources for the tool man page') 92 args = argparser.parse_args(sys.argv[1:]) 93 94 if args.v: 95 logg.setLevel(logging.DEBUG) 96 97 b = bytes.fromhex(strip_0x(args.b)) 98 flags = int.from_bytes(b, byteorder='big') 99 flags_debug = argflag.names(flags) 100 101 logg.debug('apply arg flags {}: {}'.format(flags, ', '.join(flags_debug))) 102 103 # TODO: unfortunately, if arguments are added in chainlib/cli/arg.py they still also have to be manually added in chainlib/cli/man.py 104 g = DocGenerator(flags) 105 106 toolname = args.n 107 g.process() 108 109 def apply_override(g, override_dir): 110 #if args.overrides_dir != None: 111 overrides_file = os.path.join(override_dir, toolname + '.overrides') 112 override = True 113 f = None 114 try: 115 f = open(overrides_file, 'r') 116 except FileNotFoundError: 117 logg.debug('no overrides found for {}'.format(toolname)) 118 override = False 119 120 if override: 121 while True: 122 s = f.readline() 123 if len(s) == 0: 124 break 125 v = s.split('\t', maxsplit=4) 126 fargs = None 127 try: 128 fargs = v[2].rstrip().split(',') 129 except IndexError: 130 fargs = [] 131 argvalue = None 132 if len(v) == 4: 133 argvalue = v[3] 134 try: 135 g.override_arg(v[0], v[1], fargs, argvalue=argvalue) 136 except KeyError: 137 logg.info('adding not previously registered key {} flags: {}'.format(v[0], ','.join(fargs))) 138 g.set_arg(v[0], v[1], fargs, argvalue=argvalue) 139 f.close() 140 return g 141 142 143 def get_head(tool_name, source_dir): 144 header_file = os.path.join(source_dir, tool_name + '.head.groff') 145 f = open(header_file, 'r') 146 head = f.read() 147 f.close() 148 return head 149 150 151 def get_examples(tool_name, source_dir): 152 example_file = os.path.join(source_dir, tool_name + '.examples.groff') 153 f = None 154 try: 155 f = open(example_file, 'r') 156 except FileNotFoundError: 157 logg.debug('no examples file found for {}'.format(tool_name)) 158 return None 159 logg.info('examples file {} found for {}'.format(example_file, tool_name)) 160 examples = f.read() 161 f.close() 162 return examples 163 164 165 def get_custom(tool_name, source_dir): 166 custom_file = os.path.join(source_dir, tool_name + '.custom.groff') 167 f = None 168 try: 169 f = open(custom_file, 'r') 170 except FileNotFoundError: 171 logg.debug('no custom file found for {}'.format(tool_name)) 172 return None 173 logg.info('custom file {} found for {}'.format(custom_file, tool_name)) 174 custom = f.read() 175 f.close() 176 return custom 177 178 179 def get_seealso(tool_name, source_dir): 180 seealso_file = os.path.join(source_dir, tool_name + '.seealso.groff') 181 f = None 182 try: 183 f = open(seealso_file, 'r') 184 except FileNotFoundError: 185 logg.debug('no seealso file found for {}'.format(tool_name)) 186 return None 187 logg.info('seealso file {} found for {}'.format(seealso_file, tool_name)) 188 seealso = f.read() 189 f.close() 190 return seealso 191 192 193 g = apply_override(g, args.source_dir) 194 195 ge = EnvDocGenerator(flags, override=args.overrides_env_dir) 196 ge.process() 197 198 head = get_head(toolname, args.source_dir) 199 examples = get_examples(toolname, args.source_dir) 200 custom = get_custom(toolname, args.source_dir) 201 seealso = get_seealso(toolname, args.source_dir) 202 203 if args.overrides_config_file != None: 204 f = open(args.overrides_config_file, 'r') 205 configuration_description = f.read() 206 f.close() 207 208 (fd, fp) = tempfile.mkstemp() 209 f = os.fdopen(fd, 'w') 210 f.write(head) 211 f.write(str(g)) 212 f.write(configuration_description) 213 214 if custom != None: 215 f.write(custom) 216 217 if examples != None: 218 f.write(".SH EXAMPLES\n\n") 219 f.write(examples) 220 221 if seealso != None: 222 seealso_description = seealso 223 224 if len(ge) > 0: 225 f.write(".SH ENVIRONMENT\n\n") 226 f.write(str(ge)) 227 228 f.write(legal_description) 229 f.write(source_description) 230 f.write(seealso_description) 231 f.close() 232 233 dest = os.path.join(args.d, toolname + '.1') 234 shutil.copyfile(fp, dest) 235 236 os.unlink(fp)