8076924e2be9829d7a2bc1de1e09b9d5f3f0a18c
[scm/meta/abs.git] / abs
1 #!/usr/bin/env python
2 # vim: ai ts=4 sts=4 et sw=4
3 #
4 # Copyright (c) 2014, 2015, 2016 Samsung Electronics.Co.Ltd.
5 #
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the Free
8 # Software Foundation; version 2 of the License
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 # for more details.
14 #
15
16 import sys
17 import os
18 import subprocess
19 import re
20 import argparse
21 from argparse import ArgumentParser
22 import ConfigParser
23 import glob
24 import fnmatch
25 import shutil
26 import zipfile
27 import errno
28
29 g_home = os.path.dirname(os.path.realpath(__file__))
30 class LocalError(Exception):
31     """Local error exception."""
32
33     pass
34
35 class Executor(object):
36     """Subprocess wrapper"""
37
38     def __init__(self, checker=None):
39         self.stdout = subprocess.PIPE
40         self.stderr = subprocess.STDOUT
41         self.checker = checker
42
43     def run(self, cmdline_or_args, show=False, checker=False):
44         """Execute external command"""
45
46         out = ''
47         try:
48             process = subprocess.Popen(cmdline_or_args, \
49                                        stdout=self.stdout, \
50                                        stderr=self.stderr, \
51                                        shell=True)
52             while True:
53                 line = process.stdout.readline()
54                 if show:
55                     print line.rstrip()
56                 out = out + '\n' + line.rstrip()
57                 if not line:
58                     break
59         except:
60             raise LocalError('Running process failed')
61
62         return out
63
64 def list_files(path, ext=None):
65
66     f_list = []
67     for root, dirnames, filenames in os.walk(path):
68         if ext is None:
69             for filename in filenames:
70                 f_list.append(os.path.join(root, filename))
71         else:
72             for filename in fnmatch.filter(filenames, '*.'+ext):
73                 f_list.append(os.path.join(root, filename))
74     return f_list
75
76 class FakeSecHead(object):
77
78     def __init__(self, fp):
79
80         self.fp = fp
81         self.sechead = '[ascection]\n'
82
83     def readline(self):
84
85         if self.sechead:
86             try:
87                 return self.sechead
88             finally:
89                 self.sechead = None
90         else:
91             return self.fp.readline()
92
93 class ErrorParser(object):
94     """Inspect specific error string"""
95
96     parsers = []
97
98     def __init__(self):
99
100         ErrorParser = {'GNU_LINKER':['(.*?):?\(\.\w+\+.*\): (.*)', \
101                                      '(.*[/\\\])?ld(\.exe)?: (.*)'], \
102                        'GNU_GCC':['(.*?):(\d+):(\d+:)? [Ee]rror: ([`\'"](.*)[\'"] undeclared .*)', \
103                                   '(.*?):(\d+):(\d+:)? [Ee]rror: (conflicting types for .*[`\'"](.*)[\'"].*)', \
104                                   '(.*?):(\d+):(\d+:)? (parse error before.*[`\'"](.*)[\'"].*)', \
105                                   '(.*?):(\d+):(\d+:)?\s*(([Ee]rror)|(ERROR)): (.*)'], \
106 #                                  '(.*?):(\d+):(\d+:)? (.*)'], \
107                        'GNU_GMAKE':['(.*):(\d*): (\*\*\* .*)', \
108                                     '.*make.*: \*\*\* .*', \
109                                     '.*make.*: Target (.*) not remade because of errors.', \
110                                     '.*[Cc]ommand not found.*', \
111                                     '^Error:\s*(.*)'], \
112                        'TIZEN_NATIVE':['.*ninja: build stopped.*', \
113                                        'edje_cc: Error..(.*):(\d).*', \
114                                        'edje_cc: Error.*']}
115
116         for parser in ErrorParser:
117             parser_env = os.getenv('SDK_ERROR_'+parser)
118             if parser_env:
119                 self.parsers.append(parser_env)
120             else:
121                 for msg in ErrorParser[parser]:
122                     self.parsers.append(msg)
123
124     def check(self, full_log):
125         """Check error string line by line"""
126
127         #snipset_text = full_log[:full_log.rfind('PLATFORM_VER\t')].split('\n')
128         for line in full_log.split('\n'):
129             errline = re.search('|'.join(self.parsers), line[:1024])
130             if errline:
131                 return errline.string #Errors
132         return None #No error
133
134 class _Rootstrap(object):
135     """Tizen SDK rootstrap info.
136        Used only in Sdk class"""
137
138     rootstrap_list = None
139     sdk_path = None
140
141     def __init__(self, sdk_path=None, config=None, rootstrap_search=None):
142
143         self.tizen = sdk_path
144         self.list_rootstrap(rootstrap_search)
145         self.config_file = config
146
147     def list_rootstrap(self, rootstrap_search=None):
148         """List all the rootstraps"""
149
150         rs_prefix = 'mobile|wearable'
151         if rootstrap_search is not None:
152             rs_prefix = rootstrap_search
153         print 'Set rs_prefix: %s' % rs_prefix
154
155         if self.rootstrap_list != None:
156             return self.rootstrap_list
157
158         cmdline = self.tizen + ' list rootstrap'
159         ret = Executor().run(cmdline, show=False)
160         for x in ret.splitlines():
161             if re.search('(%s)-([0-9.]*)-(device.*|emulator.*).core.*' % rs_prefix, x):
162                 if self.rootstrap_list == None:
163                     self.rootstrap_list = []
164                 self.rootstrap_list.append(x.split(' ')[0])
165             else:
166                 print 'No search result for %s' % '(%s)-([0-9.]*)-(device.*|emulator.*).core.*' % rs_prefix
167         return self.rootstrap_list
168
169     def check_rootstrap(self, rootstrap, show=True):
170         """Specific rootstrap is in the SDK
171            Otherwise use default"""
172
173         if rootstrap == None:
174             rootstrap = 'mobile-3.0-emulator.core' #default
175         if rootstrap in self.rootstrap_list:
176             return rootstrap
177         else:
178             if show == True:
179                 print '  ERROR: Rootstrap [%s] does not exist' % rootstrap
180                 print '  Update your rootstrap or use one of:\n    %s' \
181                        % '\n    '.join(self.list_rootstrap())
182             return None
183
184 class Sdk(object):
185     """Tizen SDK related job"""
186
187     rs = None #_Rootstrap class instance
188     rootstrap_list = None
189     sdk_to_search = ['tizen-studio/tools/ide/bin/tizen', \
190                      'tizen-sdk/tools/ide/bin/tizen', \
191                      'tizen-sdk-ux/tools/ide/bin/tizen', \
192                      'tizen-sdk-cli/tools/ide/bin/tizen']
193
194     def __init__(self, sdkpath=None, rootstrap_search=None):
195
196         self.error_parser = ErrorParser()
197         self.runtool = Executor(checker=self.error_parser)
198
199         self.home = os.getenv('HOME')
200         self.config_file = os.path.join(g_home, '.abs')
201
202         if sdkpath is None:
203             self.tizen = self.get_user_root()
204             if self.tizen is None or self.tizen == '':
205                 for i in self.sdk_to_search:
206                     if os.path.isfile(os.path.join(self.home, i)):
207                         self.tizen = os.path.join(self.home, i)
208                         break
209         else:
210             self.tizen = os.path.join(sdkpath, 'tools/ide/bin/tizen')
211             self.update_user_root(self.tizen)
212
213         if not os.path.isfile(self.tizen):
214             print 'Cannot locate cli tool'
215             raise LocalError('Fail to locate cli tool')
216
217         self.rs = _Rootstrap(sdk_path=self.tizen, config=self.config_file, rootstrap_search=rootstrap_search)
218
219     def get_user_root(self):
220
221         if os.path.isfile(self.config_file):
222             config = ConfigParser.RawConfigParser()
223             config.read(self.config_file)
224             return config.get('Global', 'tizen')
225         return None
226
227     def update_user_root(self, path):
228
229         if not os.path.isfile(self.config_file):
230             with open(self.config_file, 'w') as f:
231                 f.write('[Global]\n')
232                 f.write('tizen = %s\n' % path)
233             return
234
235         config = ConfigParser.RawConfigParser()
236         config.read(self.config_file)
237         config.set('Global', 'tizen', path)
238         with open(self.config_file, 'wb') as cf:
239             config.write(cf)
240
241     def list_rootstrap(self):
242         return self.rs.list_rootstrap()
243
244     def check_rootstrap(self, rootstrap):
245         return self.rs.check_rootstrap(rootstrap)
246
247     def _run(self, command, args, show=True, checker=False):
248         """Run a tizen command"""
249
250         cmd = [self.tizen, command] + args
251         if command == 'package':
252             cmd = ['dbus-run-session -- bash; echo build | gnome-keyring-daemon --unlock; '] + cmd
253         print '\nRunning command:\n    %s' % ' '.join(cmd)
254         return self.runtool.run('{}'.format(' '.join(cmd)), \
255                 show=show, checker=checker)
256
257     def copytree2(self, src, dst, symlinks=False, ignore=None):
258         """Copy with Ignore & Overwrite"""
259         names = os.listdir(src)
260         if ignore is not None:
261             ignored_names = ignore(src, names)
262         else:
263             ignored_names = set()
264
265         try:
266             os.makedirs(dst)
267         except:
268             pass
269
270         errors = []
271         for name in names:
272             if name in ignored_names:
273                 continue
274             srcname = os.path.join(src, name)
275             dstname = os.path.join(dst, name)
276             try:
277                 if symlinks and os.path.islink(srcname):
278                     linkto = os.readlink(srcname)
279                     os.symlink(linkto, dstname)
280                 elif os.path.isdir(srcname):
281                     self.copytree2(srcname, dstname, symlinks, ignore)
282                 else:
283                     # Will raise a SpecialFileError for unsupported file types
284                     shutil.copy2(srcname, dstname)
285             # catch the Error from the recursive copytree so that we can
286             # continue with other files
287             except shutil.Error, err:
288                 errors.extend(err.args[0])
289             except EnvironmentError, why:
290                 errors.append((srcname, dstname, str(why)))
291         try:
292             shutil.copystat(src, dst)
293         except OSError, why:
294             if WindowsError is not None and isinstance(why, WindowsError):
295                 # Copying file access times may fail on Windows
296                 pass
297             else:
298                 errors.append((src, dst, str(why)))
299         #if errors:
300          #   raise shutil.Error, errors
301
302     def _copy_build_output(self, src, dst):
303         if not os.path.isdir(src) :
304             return
305         try:
306             self.copytree2(src, dst, ignore=shutil.ignore_patterns('*.edc', '*.po', 'objs', '*.info', '*.so', 'CMakeLists.txt', '*.h', '*.c'))
307         except OSError as exc:
308             # File already exist
309             if exc.errno == errno.EEXIST:
310                 shutil.copy(src, dst)
311             if exc.errno == errno.ENOENT:
312                 shutil.copy(src, dst)
313             else:
314                 raise
315
316     def _package_sharedlib(self, project_path, conf, app_name):
317         """If -r option used for packaging, make zip file from copied files"""
318         #project_path=project['path']
319         project_build_output_path=os.path.join(project_path, conf)
320         package_path=os.path.join(project_build_output_path, '.pkg')
321
322         if os.path.isdir(package_path):
323             shutil.rmtree(package_path)
324         os.makedirs(package_path)
325         os.makedirs(os.path.join(package_path, 'lib'))
326
327         #Copy project resource
328         self._copy_build_output(os.path.join(project_path, 'lib'), os.path.join(package_path, 'lib'))
329         self._copy_build_output(os.path.join(project_path, 'res'), os.path.join(package_path, 'res'))
330
331         #Copy built res resource
332         self._copy_build_output(os.path.join(project_build_output_path, 'res'), os.path.join(package_path, 'res'))
333
334         #Copy so library file
335         for filename in list_files(project_build_output_path, 'so'):
336             shutil.copy(filename, os.path.join(package_path, 'lib'))
337
338         # Copy so library file
339         zipname=app_name + '.zip'
340         rsrc_zip = os.path.join(project_build_output_path, zipname)
341         myZipFile = zipfile.ZipFile(rsrc_zip, 'w')
342         for filename in list_files(package_path):
343             try:
344                 myZipFile.write(filename, filename.replace(package_path, ''))
345             except Exception, e:
346                 print str(e)
347         myZipFile.close()
348         return rsrc_zip
349
350     def build_tizen(self, source, rootstrap=None, arch=None, conf='Debug', jobs=None):
351         """SDK CLI build command"""
352
353         _rootstrap = self.check_rootstrap(rootstrap)
354         if _rootstrap == None:
355             raise LocalError('Rootstrap %s not exist' % rootstrap)
356
357         if rootstrap is None and arch is None:
358             rootstrap = _rootstrap
359             self.arch = 'x86'
360         elif arch is None:
361             if 'emulator64' in rootstrap: self.arch = 'x86_64'
362             elif 'device64' in rootstrap: self.arch = 'aarch64'
363             elif 'emulator' in rootstrap: self.arch = 'x86'
364             elif 'device' in rootstrap: self.arch = 'arm'
365         elif rootstrap is None:
366             if arch not in ['x86', 'arm', 'aarch64', 'x86_64']:
367                 raise LocalError('Architecture and rootstrap mismatch')
368
369             rootstrap = _rootstrap
370             if arch == 'x86_64': rootstrap = rootstrap.replace('emulator', 'emulator64')
371             elif arch == 'aarch64': rootstrap = rootstrap.replace('emulator', 'device64')
372             elif arch == 'arm': rootstrap = rootstrap.replace('emulator', 'device')
373
374         for x in source.project_list:
375             b_args = []
376             if x['web_app'] == True:
377                 print '\n\n BUILD WEB\n'
378                 b_args.extend(['--', x['path']])
379                 out = self._run('build-web ', b_args, checker=True)
380             else:
381                 print '\n\n BUILD NATIVE\n'
382                 if jobs is not None:
383                     b_args.extend(['-j', jobs])
384                 b_args.extend(['-r', rootstrap, '-a', self.arch, '-C', conf])
385                 b_args.extend(['--', x['path']])
386                 out = self._run('build-native', b_args, checker=True)
387             logpath = os.path.join(source.output_dir, \
388                                   'build_%s_%s' % (rootstrap, os.path.basename(x['path'])))
389             if not os.path.isdir(source.output_dir):
390                 os.makedirs(source.output_dir)
391             with open(logpath, 'w') as lf:
392                 lf.write(out)
393             ret = self.error_parser.check(out)
394             if True:
395                 with open(logpath+'.log', 'w') as lf:
396                     lf.write(out)
397             if ret:
398                 raise LocalError(ret)
399
400     def raise_package_exception(self, out, output_dir, i, bname, appname):
401         logpath = os.path.join(output_dir, \
402                               'package_%d_%s' % (i, bname))
403         if not os.path.isdir(output_dir):
404             os.makedirs(output_dir)
405         with open(logpath + '.log', 'w') as lf:
406             lf.write(out)
407         if 'keystore password was incorrect' in out:
408             raise LocalError('keystore password was incorrect for %s.' % appname)
409         raise LocalError('TPK/WGT file not generated for %s.' % appname)
410
411     def package(self, source, cert=None, pkg_type=None, conf='Debug', manual_strip=False):
412         """SDK CLI package command
413             IF Debug + Manual Strip off then generate package-name-debug.tpk
414             IF Debug + Manual Strip on then generate package-name.tpk with custom strip
415             IF Release then generate package-name.tpk with strip option
416         """
417         if cert is None: cert = 'ABS'
418         if pkg_type is None: pkg_type = 'tpk'
419         if conf is None: conf = 'Debug'
420
421         final_app = ''
422         main_args = ['-t', pkg_type, '-s', cert]
423         main_args_web = ['-t', 'wgt', '-s', cert]
424         out = '' #logfile
425
426         # remove tpk or zip file on project path
427         package_list = []
428         for i, x in enumerate(source.project_list):
429             package_list.extend(list_files(os.path.join(x['path'], conf), ext='tpk'))
430             package_list.extend(list_files(os.path.join(x['path'], conf), ext='zip'))
431             package_list.extend(list_files(x['path'], ext='wgt'))
432
433         for k in package_list :
434             print ' package list ' + k;
435             os.remove(k)
436
437         # Manual strip
438         if manual_strip == True :
439             main_args.extend(['--strip', 'on'])
440             strip_cmd='';
441             if self.arch == None:
442                 raise LocalError('Architecture is None')
443
444             for i, x in enumerate(source.project_list):
445                 dir = os.path.join(x['path'], conf)
446                 if not os.path.isdir(dir):
447                     continue
448                 files = [os.path.join(dir,f) for f in os.listdir(dir) if os.path.isfile(os.path.join(dir,f))]
449
450                 # dir must be "project/Debug directory"
451                 info_path = os.path.join(dir, "build.info")
452                 # Default Strip gcc version is 6.2
453                 gcc_version = "6.2"
454                 # Parsing GCC version from build.info inside Debug directory in Project to each project
455                 if os.path.exists(info_path) :
456                     with open(info_path) as fp :
457                         for line in fp :
458                             if line.startswith("toolchain=") :
459                                 line = line.strip()
460                                 gcc_version = re.findall("\d+\.\d+", line)[0]
461                 else :
462                     print "Cannot find Debug/build.info. The default gcc will strip tpk"
463
464                 print "gcc version:" + gcc_version
465
466                 if self.arch == 'x86' :
467                     if(gcc_version == "4.9"):
468                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i386-linux-gnueabi-gcc-' + gcc_version + '/bin/i386-linux-gnueabi-strip')
469                     else:
470                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i586-linux-gnueabi-gcc-' + gcc_version + '/bin/i586-linux-gnueabi-strip')
471                 elif self.arch == 'arm' :
472                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../arm-linux-gnueabi-gcc-' + gcc_version + '/bin/arm-linux-gnueabi-strip')
473                 elif self.arch == 'x86_64' :
474                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../x86_64-linux-gnu-gcc-' + gcc_version + '/bin/x86_64-linux-gnu-strip')
475                 elif self.arch == 'aarch64' :
476                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../aarch64-linux-gnu-gcc-' + gcc_version + '/bin/aarch64-linux-gnu-strip')
477
478                 print strip_cmd
479
480                 for k in files:
481                     cmdline = strip_cmd + ' ' + k;
482                     Executor().run(cmdline, show=False)
483
484         elif conf == 'Release':
485             main_args.extend(['--strip', 'on'])
486
487         for i, x in enumerate(source.project_list):
488             if x['web_app'] == False:
489                 if x['type'] == 'app':
490                     print '\n\n PACKAGE NATIVE\n'
491                     out = '%s\n%s' % (out, \
492                           self._run('package', main_args + ['--',os.path.join(x['path'],conf)]))
493                     try:
494                         final_app = list_files(os.path.join(x['path'], conf), ext='tpk')[0]
495                     except:
496                         self.raise_package_exception(out, source.output_dir, i, os.path.basename(x['path']), x['APPNAME'])
497                     x['out_package'] = final_app
498                 elif x['type'] == 'sharedLib':
499                     self._package_sharedlib(x['path'], conf, x['APPNAME'])
500                     x['out_package'] = list_files(os.path.join(x['path'], conf), ext='zip')[0]
501                 else:
502                     raise LocalError('Not supported project type %s' % x['type'])
503             elif x['web_app'] == True:
504                 print '\n\n PACKAGE WEB\n'
505                 out = '%s\n%s' % (out, \
506                       self._run('package', main_args_web + ['--', os.path.join(x['path'], '.buildResult')]))
507                 try:
508                     final_app = list_files(os.path.join(x['path'], '.buildResult'), ext='wgt')[0]
509                 except:
510                     self.raise_package_exception(out, source.output_dir, i, os.path.basename(x['path']), x['APPNAME'])
511                 x['out_package'] = final_app
512
513         if source.b_multi == True:
514             extra_args=[]
515             print 'THIS IS MULTI PROJECT'
516             for i, x in enumerate(source.project_list):
517                 if x['out_package'] != final_app and x.get('type') == 'app':
518                     extra_args.extend(['-r', '"%s"' % x['out_package']])
519                 elif x.get('type') == 'sharedLib':
520                     extra_args.extend(['-r', '"%s"' % x['out_package']])
521
522             extra_args.extend(['--', '"%s"' % final_app])
523             if final_app.endswith('.tpk'):
524                 out = '%s\n\n%s' % (out, self._run('package', main_args + extra_args))
525             elif final_app.endswith('.wgt'):
526                 out = '%s\n\n%s' % (out, self._run('package', main_args_web + extra_args))
527
528         #TODO: signature validation check failed : Invalid file reference. An unsigned file was found.
529         if final_app.endswith('.tpk'):
530             print 'Packaging final step again!'
531             out = '%s\n\n%s' % (out, self._run('package', main_args + ['--', '"%s"' % final_app]))
532
533         #Append arch to web binary
534         #if final_app.endswith('.wgt'):
535         #    final_app_with_arch = final_app.replace('.wgt', '-%s.wgt' % self.arch)
536         #    os.rename(final_app, final_app_with_arch)
537         #    final_app = final_app_with_arch
538
539         #Copy tpk to output directory
540         if conf == 'Debug' and manual_strip == False :
541             basename = os.path.splitext(final_app)[0]
542             if final_app.endswith('.tpk'):
543                 newname = basename +'-debug.tpk'
544             elif final_app.endswith('.wgt'):
545                 newname = basename +'-debug.wgt'
546             os.rename(final_app, newname)
547             shutil.copy(newname, source.output_dir)
548         else :
549             shutil.copy(final_app, source.output_dir)
550
551
552     def clean(self, source):
553         """SDK CLI clean command"""
554
555         if os.path.isdir(source.multizip_path):
556             shutil.rmtree(source.multizip_path)
557
558         if os.path.isfile(os.path.join(source.multizip_path, '.zip')):
559             os.remove(os.path.join(source.multizip_path, '.zip'))
560
561         for x in source.project_list:
562             self._run('clean', ['--', x['path']], show=False)
563
564 class Source(object):
565     """Project source related job"""
566
567     workspace = '' #Project root directory
568     project_list = []
569     b_multi = False
570     multi_conf_file = 'WORKSPACE' #Assume multi-project if this file exist.
571     multizip_path = '' #For multi-project packaging -r option
572     property_dict = {}
573     output_dir = '_abs_out_'
574
575     def __init__(self, src=None):
576
577         if src == None:
578             self.workspace = os.getcwd()
579         else:
580             self.workspace = os.path.abspath(src)
581         self.output_dir = os.path.join(self.workspace, self.output_dir)
582
583         os.environ['workspace_loc']=str(os.path.realpath(self.workspace))
584
585         self.multizip_path = os.path.join(self.workspace, 'multizip')
586         self.pre_process()
587
588     def set_properties(self, path):
589         """Fetch all properties from project_def.prop"""
590
591         mydict = {}
592         cp = ConfigParser.SafeConfigParser()
593         cp.optionxform = str
594         if self.is_web_app(path):
595             mydict['web_app'] = True
596         else:
597             mydict['web_app'] = False
598             cp.readfp(FakeSecHead(open(os.path.join(path, 'project_def.prop'))))
599             for x in cp.items('ascection'):
600                 mydict[x[0]] = x[1]
601         mydict['path'] = path
602         return mydict
603
604     def set_user_options(self, c_opts=None, cpp_opts=None, link_opts=None):
605         if c_opts is not None:
606             os.environ['USER_C_OPTS'] = c_opts
607             print 'Set USER_C_OPTS=[%s]' % os.getenv('USER_C_OPTS')
608         if cpp_opts is not None:
609             os.environ['USER_CPP_OPTS'] = cpp_opts
610             print 'Set USER_CPP_OPTS=[%s]' % os.getenv('USER_CPP_OPTS')
611         if link_opts is not None:
612             os.environ['USER_LINK_OPTS'] = link_opts
613             print 'Set USER_LINK_OPTS=[%s]' % os.getenv('USER_LINK_OPTS')
614
615     def is_web_app(self, project_directory):
616         if os.path.isfile(os.path.join(project_directory, 'config.xml')):
617             return True
618         return False
619
620     def pre_process(self):
621
622         if os.path.isfile(os.path.join(self.workspace, self.multi_conf_file)):
623             self.b_multi = True
624             with open(os.path.join(self.workspace, self.multi_conf_file)) as f:
625                 for line in f:
626                     if not line.strip():
627                         continue
628                     file_path = os.path.join(self.workspace, line.rstrip())
629                     self.project_list.append(self.set_properties(file_path))
630         else:
631             self.b_multi = False
632             file_path = os.path.join(self.workspace)
633             self.project_list.append(self.set_properties(file_path))
634
635 def argument_parsing(argv):
636     """Any arguments passed from user"""
637
638     parser = argparse.ArgumentParser(description='ABS command line interface')
639
640     subparsers = parser.add_subparsers(dest='subcommands')
641
642     #### [subcommand - BUILD] ####
643     build = subparsers.add_parser('build')
644     build.add_argument('-w', '--workspace', action='store', dest='workspace', \
645                         help='source directory')
646     build.add_argument('-r', '--rootstrap', action='store', dest='rootstrap', \
647                         help='(ex, mobile-3.0-device.core) rootstrap name')
648     build.add_argument('-a', '--arch', action='store', dest='arch', \
649                         help='(x86|arm|x86_64|aarch64) Architecture to build')
650     build.add_argument('-t', '--type', action='store', dest='type', \
651                         help='(tpk|wgt) Packaging type')
652     build.add_argument('-s', '--cert', action='store', dest='cert', \
653                         help='(ex, ABS) Certificate profile name')
654     build.add_argument('-c', '--conf', action='store',default='Release', dest='conf', \
655                         help='(ex, Debug|Release) Build Configuration')
656     build.add_argument('-j', '--jobs', action='store', dest='jobs', \
657                         help='(number of jobs) The number of parallel builds')
658     build.add_argument('--sdkpath', action='store', dest='sdkpath', \
659                         help='Specify Tizen SDK installation root (one time init).' \
660                              ' ex) /home/yours/tizen-sdk/')
661     build.add_argument('--profile-to-search', action='store', dest='profiletosearch', \
662                         help='Rootstrap profile prefix.' \
663                              ' ex) (mobile|wearable|da-hfp)')
664     build.add_argument('--c-opts', action='store', dest='c_opts', \
665                         help='Extra compile options USER_C_OPTS')
666     build.add_argument('--cpp-opts', action='store', dest='cpp_opts', \
667                         help='Extra compile options USER_CPP_OPTS')
668     build.add_argument('--link-opts', action='store', dest='link_opts', \
669                         help='Extra linking options USER_LINK_OPTS')
670
671     return parser.parse_args(argv[1:])
672
673 def build_main(args):
674     """Command [build] entry point."""
675
676     try:
677         my_source = Source(src=args.workspace)
678
679         my_source.set_user_options(c_opts=args.c_opts, cpp_opts=args.cpp_opts, link_opts=args.link_opts)
680         print '-------------------'
681         print '(%s)' % args.profiletosearch
682         print '-------------------'
683         my_sdk = Sdk(sdkpath=args.sdkpath, rootstrap_search=args.profiletosearch)
684         my_sdk.clean(my_source)
685         my_sdk.build_tizen(my_source, rootstrap=args.rootstrap, arch=args.arch, jobs=args.jobs)
686         if args.conf == 'Debug' :
687             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert)
688             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
689         else :
690             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
691
692     except Exception as err:
693         wrk = os.path.join(os.path.abspath(args.workspace), '_abs_out_')
694         if not os.path.isdir(wrk):
695             os.makedirs(wrk)
696         with open(os.path.join(wrk, 'build_EXCEPTION.log'), 'w') as ef:
697             ef.write('Exception %s' % str(err))
698         raise err
699
700 def main(argv):
701     """Script entry point."""
702
703     args = argument_parsing(argv)
704
705     if args.subcommands == 'build':
706         return build_main(args)
707     else:
708         print 'Unsupported command %s' % args.subcommands
709         raise LocalError('Command %s not supported' % args.subcommands)
710
711 if __name__ == '__main__':
712
713     try:
714         sys.exit(main(sys.argv))
715     except Exception, e:
716         print 'Exception %s' % str(e)
717         #FIXME: Remove hard-coded output directory.
718         if not os.path.isdir('_abs_out_'):
719             os.makedirs('_abs_out_')
720         with open(os.path.join('_abs_out_', 'build_EXCEPTION.log'), 'w') as ef:
721             ef.write('Exception %s' % repr(e))
722         if 'keystore password was incorrect' in repr(e):
723             sys.exit(99)
724         sys.exit(77)