Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / compiler / one-cmds / one-codegen
1 #!/usr/bin/env bash
2 ''''export SCRIPT_PATH="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" # '''
3 ''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python                                       # '''
4 ''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@"                                     # '''
5 ''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
6 ''''exit 255                                                                            # '''
7
8 # Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
9 #
10 # Licensed under the Apache License, Version 2.0 (the "License");
11 # you may not use this file except in compliance with the License.
12 # You may obtain a copy of the License at
13 #
14 #    http://www.apache.org/licenses/LICENSE-2.0
15 #
16 # Unless required by applicable law or agreed to in writing, software
17 # distributed under the License is distributed on an "AS IS" BASIS,
18 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 # See the License for the specific language governing permissions and
20 # limitations under the License.
21
22 import argparse
23 import copy
24 import glob
25 import itertools
26 import ntpath
27 import os
28 import sys
29 import shutil
30 from types import SimpleNamespace
31
32 import onelib.backends as backends
33 import onelib.utils as oneutils
34
35 # TODO Find better way to suppress trackback on error
36 sys.tracebacklimit = 0
37
38
39 def _get_parser(backends_list):
40     codegen_usage = 'one-codegen [-h] [-v] [-C CONFIG] [-b BACKEND] [--] [COMMANDS FOR BACKEND]'
41     parser = argparse.ArgumentParser(
42         description='command line tool for code generation', usage=codegen_usage)
43
44     oneutils.add_default_arg(parser)
45
46     # get backend list in the directory
47     backends_name = [ntpath.basename(f) for f in backends_list]
48     if not backends_name:
49         backends_name_message = '(There is no available backend drivers)'
50     else:
51         backends_name_message = '(available backend drivers: ' + ', '.join(
52             backends_name) + ')'
53     backend_help_message = 'backend name to use ' + backends_name_message
54     parser.add_argument('-b', '--backend', type=str, help=backend_help_message)
55
56     return parser
57
58
59 def _verify_arg(parser, args, cfg_args, backend_args, unknown_args):
60     """verify given arguments"""
61     cmd_backend_exist = oneutils.is_valid_attr(args, 'backend')
62     cfg_backend_exist = oneutils.is_valid_attr(cfg_args, 'backend')
63     cfg_backends_exist = oneutils.is_valid_attr(cfg_args, 'backends')
64
65     # check if required arguments is given
66     missing = []
67     if not cmd_backend_exist and not cfg_backend_exist and not cfg_backends_exist:
68         missing.append('-b/--backend')
69     if len(missing):
70         parser.error('the following arguments are required: ' + ' '.join(missing))
71
72     if not oneutils.is_valid_attr(args, 'config'):
73         if not backend_args and not unknown_args:
74             parser.error('commands for the backend is missing.')
75
76     if cfg_backend_exist and cfg_backends_exist:
77         parser.error(
78             '\'backend\' option and \'backends\' option cannot be used simultaneously.')
79
80     # Check if given backend from command line exists in the configuration file
81     if cmd_backend_exist and cfg_backend_exist:
82         if args.backend != cfg_args.backend:
83             parser.error('Not found the command of given backend')
84
85     if cfg_backend_exist and not oneutils.is_valid_attr(cfg_args, 'command'):
86         parser.error('\'command\' key is missing in the configuration file.')
87
88     if cfg_backends_exist:
89         cfg_backends = getattr(cfg_args, 'backends').split(',')
90         # check if commands of given backends exist
91         for b in cfg_backends:
92             if not oneutils.is_valid_attr(cfg_args, b):
93                 parser.error('Not found the command for ' + b)
94
95         # Check if given backend from command line exists in the configuration file
96         if cmd_backend_exist:
97             if args.backend not in cfg_backends:
98                 parser.error('Not found the command of given backend')
99
100
101 def _parse_arg(parser):
102     codegen_args = []
103     backend_args = []
104     unknown_args = []
105     argv = copy.deepcopy(sys.argv)
106     # delete file name
107     del argv[0]
108     # split by '--'
109     args = [list(y) for x, y in itertools.groupby(argv, lambda z: z == '--') if not x]
110     if len(args) == 0:
111         codegen_args = parser.parse_args(codegen_args)
112     # one-codegen has two interfaces
113     # 1. one-codegen [-h] [-v] [-C CONFIG] [-b BACKEND] [COMMANDS FOR BACKEND]
114     if len(args) == 1:
115         codegen_args = args[0]
116         codegen_args, unknown_args = parser.parse_known_args(codegen_args)
117     # 2. one-codegen [-h] [-v] [-C CONFIG] [-b BACKEND] -- [COMMANDS FOR BACKEND]
118     if len(args) == 2:
119         codegen_args = args[0]
120         backend_args = args[1]
121         codegen_args = parser.parse_args(codegen_args)
122     # print version
123     if len(args) and codegen_args.version:
124         oneutils.print_version_and_exit(__file__)
125
126     return codegen_args, backend_args, unknown_args
127
128
129 def main():
130     # get backend list
131     backends_list = backends.get_list('compile')
132
133     # parse arguments
134     parser = _get_parser(backends_list)
135     args, backend_args, unknown_args = _parse_arg(parser)
136
137     # parse configuration file
138     cfg_args = SimpleNamespace()
139     oneutils.parse_cfg(args.config, 'one-codegen', cfg_args)
140
141     # parse configuration file (args has arguments parsed from command line + cfg)
142     # oneutils.parse_cfg(args.config, 'one-codegen', args)
143
144     # verify arguments
145     _verify_arg(parser, args, cfg_args, backend_args, unknown_args)
146     '''
147     one-codegen defines its behavior for below cases.
148
149     [1] one-codegen -h
150     [2] one-codegen -v
151     [3] one-codegen -C ${cfg} (backend, command key in cfg)
152     [4] one-codegen -C ${cfg} (backends key in cfg)
153     [5] one-codegen -b ${backend} ${command}
154     [6] one-codegen -b ${backend} -- ${command}
155     [7] one-codegen -b ${backend} -C {cfg} (backend, command key in cfg)
156     [8] one-codegen -b ${backend} -C {cfg} (backends key in cfg) (Only 'backend' is invoked, 
157          even though cfg file has multiple backends)
158     [9] one-codegen -b ${backend} -C ${cfg} -- ${command} (backend, command key in cfg) 
159     [10] one-codegen -b ${backend} -C ${cfg} -- ${command} (backends key in cfg) (Only 'backend' is invoked, 
160          even though cfg file has multiple backends)
161
162     All other cases are not allowed or an undefined behavior.
163     '''
164     cmd_overwrite = False
165     if oneutils.is_valid_attr(args, 'config'):
166         # [9], [10]
167         if backend_args and not unknown_args:
168             given_backends = [args.backend]
169             cmd_overwrite = True
170         else:
171             # [7], [8]
172             if oneutils.is_valid_attr(args, 'backend'):
173                 given_backends = [args.backend]
174                 if oneutils.is_valid_attr(cfg_args, 'backend'):
175                     assert (oneutils.is_valid_attr(cfg_args, 'command'))
176                     setattr(cfg_args, args.backend, cfg_args.command)
177             else:
178                 # [3]
179                 if oneutils.is_valid_attr(cfg_args, 'backend'):
180                     assert (oneutils.is_valid_attr(cfg_args, 'command'))
181                     given_backends = [cfg_args.backend]
182                     setattr(cfg_args, cfg_args.backend, cfg_args.command)
183                 # [4]
184                 if oneutils.is_valid_attr(cfg_args, 'backends'):
185                     given_backends = cfg_args.backends.split(',')
186     # [5], [6]
187     else:
188         assert (backend_args or unknown_args)
189         given_backends = [args.backend]
190
191     for given_backend in given_backends:
192         # make a command to run given backend driver
193         codegen_path = None
194         backend_base = given_backend + '-compile'
195         for cand in backends_list:
196             if ntpath.basename(cand) == backend_base:
197                 codegen_path = cand
198         if not codegen_path:
199             # Find backend from system path
200             codegen_path = shutil.which(backend_base)
201
202         if not codegen_path:
203             raise FileNotFoundError(backend_base + ' not found')
204
205         codegen_cmd = [codegen_path]
206         if not cmd_overwrite and oneutils.is_valid_attr(cfg_args, given_backend):
207             codegen_cmd += getattr(cfg_args, given_backend).split()
208         else:
209             codegen_cmd += backend_args
210             codegen_cmd += unknown_args
211
212         # run backend driver
213         oneutils.run(codegen_cmd, err_prefix=backend_base)
214
215
216 if __name__ == '__main__':
217     oneutils.safemain(main, __file__)