Add no-dbus parameter in case of local test
[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, no_dbus=False):
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         if no_dbus != False:
219             self.no_dbus = True
220         else:
221             self.no_dbus = False
222
223     def get_user_root(self):
224
225         if os.path.isfile(self.config_file):
226             config = ConfigParser.RawConfigParser()
227             config.read(self.config_file)
228             return config.get('Global', 'tizen')
229         return None
230
231     def update_user_root(self, path):
232
233         if not os.path.isfile(self.config_file):
234             with open(self.config_file, 'w') as f:
235                 f.write('[Global]\n')
236                 f.write('tizen = %s\n' % path)
237             return
238
239         config = ConfigParser.RawConfigParser()
240         config.read(self.config_file)
241         config.set('Global', 'tizen', path)
242         with open(self.config_file, 'wb') as cf:
243             config.write(cf)
244
245     def list_rootstrap(self):
246         return self.rs.list_rootstrap()
247
248     def check_rootstrap(self, rootstrap):
249         return self.rs.check_rootstrap(rootstrap)
250
251     def _run(self, command, args, show=True, checker=False):
252         """Run a tizen command"""
253
254         cmd = [self.tizen, command] + args
255         if command == 'package':
256             dbus_command = 'dbus-run-session -- bash; echo build | gnome-keyring-daemon --unlock;  '
257             if self.no_dbus == True:
258                 dbus_command = ' '
259             cmd = ['{} '.format(dbus_command)] + cmd
260         print '\nRunning command:\n    %s' % ' '.join(cmd)
261         return self.runtool.run('{}'.format(' '.join(cmd)), \
262                 show=show, checker=checker)
263
264     def copytree2(self, src, dst, symlinks=False, ignore=None):
265         """Copy with Ignore & Overwrite"""
266         names = os.listdir(src)
267         if ignore is not None:
268             ignored_names = ignore(src, names)
269         else:
270             ignored_names = set()
271
272         try:
273             os.makedirs(dst)
274         except:
275             pass
276
277         errors = []
278         for name in names:
279             if name in ignored_names:
280                 continue
281             srcname = os.path.join(src, name)
282             dstname = os.path.join(dst, name)
283             try:
284                 if symlinks and os.path.islink(srcname):
285                     linkto = os.readlink(srcname)
286                     os.symlink(linkto, dstname)
287                 elif os.path.isdir(srcname):
288                     self.copytree2(srcname, dstname, symlinks, ignore)
289                 else:
290                     # Will raise a SpecialFileError for unsupported file types
291                     shutil.copy2(srcname, dstname)
292             # catch the Error from the recursive copytree so that we can
293             # continue with other files
294             except shutil.Error, err:
295                 errors.extend(err.args[0])
296             except EnvironmentError, why:
297                 errors.append((srcname, dstname, str(why)))
298         try:
299             shutil.copystat(src, dst)
300         except OSError, why:
301             if WindowsError is not None and isinstance(why, WindowsError):
302                 # Copying file access times may fail on Windows
303                 pass
304             else:
305                 errors.append((src, dst, str(why)))
306         #if errors:
307          #   raise shutil.Error, errors
308
309     def _copy_build_output(self, src, dst):
310         if not os.path.isdir(src) :
311             return
312         try:
313             self.copytree2(src, dst, ignore=shutil.ignore_patterns('*.edc', '*.po', 'objs', '*.info', '*.so', 'CMakeLists.txt', '*.h', '*.c'))
314         except OSError as exc:
315             # File already exist
316             if exc.errno == errno.EEXIST:
317                 shutil.copy(src, dst)
318             if exc.errno == errno.ENOENT:
319                 shutil.copy(src, dst)
320             else:
321                 raise
322
323     def _package_sharedlib(self, project_path, conf, app_name):
324         """If -r option used for packaging, make zip file from copied files"""
325         #project_path=project['path']
326         project_build_output_path=os.path.join(project_path, conf)
327         package_path=os.path.join(project_build_output_path, '.pkg')
328
329         if os.path.isdir(package_path):
330             shutil.rmtree(package_path)
331         os.makedirs(package_path)
332         os.makedirs(os.path.join(package_path, 'lib'))
333
334         #Copy project resource
335         self._copy_build_output(os.path.join(project_path, 'lib'), os.path.join(package_path, 'lib'))
336         self._copy_build_output(os.path.join(project_path, 'res'), os.path.join(package_path, 'res'))
337
338         #Copy built res resource
339         self._copy_build_output(os.path.join(project_build_output_path, 'res'), os.path.join(package_path, 'res'))
340
341         #Copy so library file
342         for filename in list_files(project_build_output_path, 'so'):
343             shutil.copy(filename, os.path.join(package_path, 'lib'))
344
345         # Copy so library file
346         zipname=app_name + '.zip'
347         rsrc_zip = os.path.join(project_build_output_path, zipname)
348         myZipFile = zipfile.ZipFile(rsrc_zip, 'w')
349         for filename in list_files(package_path):
350             try:
351                 myZipFile.write(filename, filename.replace(package_path, ''))
352             except Exception, e:
353                 print str(e)
354         myZipFile.close()
355         return rsrc_zip
356
357     def build_tizen(self, source, rootstrap=None, arch=None, conf='Debug', jobs=None):
358         """SDK CLI build command"""
359
360         _rootstrap = self.check_rootstrap(rootstrap)
361         if _rootstrap == None:
362             raise LocalError('Rootstrap %s not exist' % rootstrap)
363
364         if rootstrap is None and arch is None:
365             rootstrap = _rootstrap
366             self.arch = 'x86'
367         elif arch is None:
368             if 'emulator64' in rootstrap: self.arch = 'x86_64'
369             elif 'device64' in rootstrap: self.arch = 'aarch64'
370             elif 'emulator' in rootstrap: self.arch = 'x86'
371             elif 'device' in rootstrap: self.arch = 'arm'
372         elif rootstrap is None:
373             if arch not in ['x86', 'arm', 'aarch64', 'x86_64']:
374                 raise LocalError('Architecture and rootstrap mismatch')
375
376             rootstrap = _rootstrap
377             if arch == 'x86_64': rootstrap = rootstrap.replace('emulator', 'emulator64')
378             elif arch == 'aarch64': rootstrap = rootstrap.replace('emulator', 'device64')
379             elif arch == 'arm': rootstrap = rootstrap.replace('emulator', 'device')
380
381         for x in source.project_list:
382             b_args = []
383             if x['web_app'] == True:
384                 print '\n\n BUILD WEB\n'
385                 b_args.extend(['--', x['path']])
386                 out = self._run('build-web ', b_args, checker=True)
387             else:
388                 print '\n\n BUILD NATIVE\n'
389                 if jobs is not None:
390                     b_args.extend(['-j', jobs])
391                 b_args.extend(['-r', rootstrap, '-a', self.arch, '-C', conf])
392                 b_args.extend(['--', x['path']])
393                 out = self._run('build-native', b_args, checker=True)
394             logpath = os.path.join(source.output_dir, \
395                                   'build_%s_%s' % (rootstrap, os.path.basename(x['path'])))
396             if not os.path.isdir(source.output_dir):
397                 os.makedirs(source.output_dir)
398             with open(logpath, 'w') as lf:
399                 lf.write(out)
400             ret = self.error_parser.check(out)
401             if True:
402                 with open(logpath+'.log', 'w') as lf:
403                     lf.write(out)
404             if ret:
405                 raise LocalError(ret)
406
407     def raise_package_exception(self, out, output_dir, i, bname, appname):
408         logpath = os.path.join(output_dir, \
409                               'package_%d_%s' % (i, bname))
410         if not os.path.isdir(output_dir):
411             os.makedirs(output_dir)
412         with open(logpath + '.log', 'w') as lf:
413             lf.write(out)
414         if 'keystore password was incorrect' in out:
415             raise LocalError('keystore password was incorrect for %s.' % appname)
416         raise LocalError('TPK/WGT file not generated for %s.' % appname)
417
418     def package(self, source, cert=None, pkg_type=None, conf='Debug', manual_strip=False):
419         """SDK CLI package command
420             IF Debug + Manual Strip off then generate package-name-debug.tpk
421             IF Debug + Manual Strip on then generate package-name.tpk with custom strip
422             IF Release then generate package-name.tpk with strip option
423         """
424         if cert is None: cert = 'ABS'
425         if pkg_type is None: pkg_type = 'tpk'
426         if conf is None: conf = 'Debug'
427
428         final_app = ''
429         main_args = ['-t', pkg_type, '-s', cert]
430         main_args_web = ['-t', 'wgt', '-s', cert]
431         out = '' #logfile
432
433         # remove tpk or zip file on project path
434         package_list = []
435         for i, x in enumerate(source.project_list):
436             package_list.extend(list_files(os.path.join(x['path'], conf), ext='tpk'))
437             package_list.extend(list_files(os.path.join(x['path'], conf), ext='zip'))
438             package_list.extend(list_files(x['path'], ext='wgt'))
439
440         for k in package_list :
441             print ' package list ' + k;
442             os.remove(k)
443
444         # Manual strip
445         if manual_strip == True :
446             main_args.extend(['--strip', 'on'])
447             strip_cmd='';
448             if self.arch == None:
449                 raise LocalError('Architecture is None')
450
451             for i, x in enumerate(source.project_list):
452                 dir = os.path.join(x['path'], conf)
453                 if not os.path.isdir(dir):
454                     continue
455                 files = [os.path.join(dir,f) for f in os.listdir(dir) if os.path.isfile(os.path.join(dir,f))]
456
457                 # dir must be "project/Debug directory"
458                 info_path = os.path.join(dir, "build.info")
459                 # Default Strip gcc version is 6.2
460                 gcc_version = "6.2"
461                 # Parsing GCC version from build.info inside Debug directory in Project to each project
462                 if os.path.exists(info_path) :
463                     with open(info_path) as fp :
464                         for line in fp :
465                             if line.startswith("toolchain=") :
466                                 line = line.strip()
467                                 gcc_version = re.findall("\d+\.\d+", line)[0]
468                 else :
469                     print "Cannot find Debug/build.info. The default gcc will strip tpk"
470
471                 print "gcc version:" + gcc_version
472
473                 if self.arch == 'x86' :
474                     if(gcc_version == "4.9"):
475                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i386-linux-gnueabi-gcc-' + gcc_version + '/bin/i386-linux-gnueabi-strip')
476                     else:
477                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i586-linux-gnueabi-gcc-' + gcc_version + '/bin/i586-linux-gnueabi-strip')
478                 elif self.arch == 'arm' :
479                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../arm-linux-gnueabi-gcc-' + gcc_version + '/bin/arm-linux-gnueabi-strip')
480                 elif self.arch == 'x86_64' :
481                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../x86_64-linux-gnu-gcc-' + gcc_version + '/bin/x86_64-linux-gnu-strip')
482                 elif self.arch == 'aarch64' :
483                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../aarch64-linux-gnu-gcc-' + gcc_version + '/bin/aarch64-linux-gnu-strip')
484
485                 print strip_cmd
486
487                 for k in files:
488                     cmdline = strip_cmd + ' ' + k;
489                     Executor().run(cmdline, show=False)
490
491         elif conf == 'Release':
492             main_args.extend(['--strip', 'on'])
493
494         for i, x in enumerate(source.project_list):
495             if x['web_app'] == False:
496                 if x['type'] == 'app':
497                     print '\n\n PACKAGE NATIVE\n'
498                     out = '%s\n%s' % (out, \
499                           self._run('package', main_args + ['--',os.path.join(x['path'],conf)]))
500                     try:
501                         final_app = list_files(os.path.join(x['path'], conf), ext='tpk')[0]
502                     except:
503                         self.raise_package_exception(out, source.output_dir, i, os.path.basename(x['path']), x['APPNAME'])
504                     x['out_package'] = final_app
505                 elif x['type'] == 'sharedLib':
506                     self._package_sharedlib(x['path'], conf, x['APPNAME'])
507                     x['out_package'] = list_files(os.path.join(x['path'], conf), ext='zip')[0]
508                 else:
509                     raise LocalError('Not supported project type %s' % x['type'])
510             elif x['web_app'] == True:
511                 print '\n\n PACKAGE WEB\n'
512                 out = '%s\n%s' % (out, \
513                       self._run('package', main_args_web + ['--', os.path.join(x['path'], '.buildResult')]))
514                 try:
515                     final_app = list_files(os.path.join(x['path'], '.buildResult'), ext='wgt')[0]
516                 except:
517                     self.raise_package_exception(out, source.output_dir, i, os.path.basename(x['path']), x['APPNAME'])
518                 x['out_package'] = final_app
519
520         if source.b_multi == True:
521             extra_args=[]
522             print 'THIS IS MULTI PROJECT'
523             for i, x in enumerate(source.project_list):
524                 if x['out_package'] != final_app and x.get('type') == 'app':
525                     extra_args.extend(['-r', '"%s"' % x['out_package']])
526                 elif x.get('type') == 'sharedLib':
527                     extra_args.extend(['-r', '"%s"' % x['out_package']])
528
529             extra_args.extend(['--', '"%s"' % final_app])
530             if final_app.endswith('.tpk'):
531                 out = '%s\n\n%s' % (out, self._run('package', main_args + extra_args))
532             elif final_app.endswith('.wgt'):
533                 out = '%s\n\n%s' % (out, self._run('package', main_args_web + extra_args))
534
535         #TODO: signature validation check failed : Invalid file reference. An unsigned file was found.
536         if final_app.endswith('.tpk'):
537             print 'Packaging final step again!'
538             out = '%s\n\n%s' % (out, self._run('package', main_args + ['--', '"%s"' % final_app]))
539
540         #Append arch to web binary
541         #if final_app.endswith('.wgt'):
542         #    final_app_with_arch = final_app.replace('.wgt', '-%s.wgt' % self.arch)
543         #    os.rename(final_app, final_app_with_arch)
544         #    final_app = final_app_with_arch
545
546         #Copy tpk to output directory
547         if conf == 'Debug' and manual_strip == False :
548             basename = os.path.splitext(final_app)[0]
549             if final_app.endswith('.tpk'):
550                 newname = basename +'-debug.tpk'
551             elif final_app.endswith('.wgt'):
552                 newname = basename +'-debug.wgt'
553             os.rename(final_app, newname)
554             shutil.copy(newname, source.output_dir)
555         else :
556             shutil.copy(final_app, source.output_dir)
557
558
559     def clean(self, source):
560         """SDK CLI clean command"""
561
562         if os.path.isdir(source.multizip_path):
563             shutil.rmtree(source.multizip_path)
564
565         if os.path.isfile(os.path.join(source.multizip_path, '.zip')):
566             os.remove(os.path.join(source.multizip_path, '.zip'))
567
568         for x in source.project_list:
569             self._run('clean', ['--', x['path']], show=False)
570
571 class Source(object):
572     """Project source related job"""
573
574     workspace = '' #Project root directory
575     project_list = []
576     b_multi = False
577     multi_conf_file = 'WORKSPACE' #Assume multi-project if this file exist.
578     multizip_path = '' #For multi-project packaging -r option
579     property_dict = {}
580     output_dir = '_abs_out_'
581
582     def __init__(self, src=None):
583
584         if src == None:
585             self.workspace = os.getcwd()
586         else:
587             self.workspace = os.path.abspath(src)
588         self.output_dir = os.path.join(self.workspace, self.output_dir)
589
590         os.environ['workspace_loc']=str(os.path.realpath(self.workspace))
591
592         self.multizip_path = os.path.join(self.workspace, 'multizip')
593         self.pre_process()
594
595     def set_properties(self, path):
596         """Fetch all properties from project_def.prop"""
597
598         mydict = {}
599         cp = ConfigParser.SafeConfigParser()
600         cp.optionxform = str
601         if self.is_web_app(path):
602             mydict['web_app'] = True
603         else:
604             mydict['web_app'] = False
605             cp.readfp(FakeSecHead(open(os.path.join(path, 'project_def.prop'))))
606             for x in cp.items('ascection'):
607                 mydict[x[0]] = x[1]
608         mydict['path'] = path
609         return mydict
610
611     def set_user_options(self, c_opts=None, cpp_opts=None, link_opts=None):
612         if c_opts is not None:
613             os.environ['USER_C_OPTS'] = c_opts
614             print 'Set USER_C_OPTS=[%s]' % os.getenv('USER_C_OPTS')
615         if cpp_opts is not None:
616             os.environ['USER_CPP_OPTS'] = cpp_opts
617             print 'Set USER_CPP_OPTS=[%s]' % os.getenv('USER_CPP_OPTS')
618         if link_opts is not None:
619             os.environ['USER_LINK_OPTS'] = link_opts
620             print 'Set USER_LINK_OPTS=[%s]' % os.getenv('USER_LINK_OPTS')
621
622     def is_web_app(self, project_directory):
623         if os.path.isfile(os.path.join(project_directory, 'config.xml')):
624             return True
625         return False
626
627     def pre_process(self):
628
629         if os.path.isfile(os.path.join(self.workspace, self.multi_conf_file)):
630             self.b_multi = True
631             with open(os.path.join(self.workspace, self.multi_conf_file)) as f:
632                 for line in f:
633                     if not line.strip():
634                         continue
635                     file_path = os.path.join(self.workspace, line.rstrip())
636                     self.project_list.append(self.set_properties(file_path))
637         else:
638             self.b_multi = False
639             file_path = os.path.join(self.workspace)
640             self.project_list.append(self.set_properties(file_path))
641
642 def argument_parsing(argv):
643     """Any arguments passed from user"""
644
645     parser = argparse.ArgumentParser(description='ABS command line interface')
646
647     subparsers = parser.add_subparsers(dest='subcommands')
648
649     #### [subcommand - BUILD] ####
650     build = subparsers.add_parser('build')
651     build.add_argument('-w', '--workspace', action='store', dest='workspace', \
652                         help='source directory')
653     build.add_argument('-r', '--rootstrap', action='store', dest='rootstrap', \
654                         help='(ex, mobile-3.0-device.core) rootstrap name')
655     build.add_argument('-a', '--arch', action='store', dest='arch', \
656                         help='(x86|arm|x86_64|aarch64) Architecture to build')
657     build.add_argument('-t', '--type', action='store', dest='type', \
658                         help='(tpk|wgt) Packaging type')
659     build.add_argument('-s', '--cert', action='store', dest='cert', \
660                         help='(ex, ABS) Certificate profile name')
661     build.add_argument('-c', '--conf', action='store',default='Release', dest='conf', \
662                         help='(ex, Debug|Release) Build Configuration')
663     build.add_argument('-j', '--jobs', action='store', dest='jobs', \
664                         help='(number of jobs) The number of parallel builds')
665     build.add_argument('--sdkpath', action='store', dest='sdkpath', \
666                         help='Specify Tizen SDK installation root (one time init).' \
667                              ' ex) /home/yours/tizen-sdk/')
668     build.add_argument('--profile-to-search', action='store', dest='profiletosearch', \
669                         help='Rootstrap profile prefix.' \
670                              ' ex) (mobile|wearable|da-hfp)')
671     build.add_argument('--c-opts', action='store', dest='c_opts', \
672                         help='Extra compile options USER_C_OPTS')
673     build.add_argument('--cpp-opts', action='store', dest='cpp_opts', \
674                         help='Extra compile options USER_CPP_OPTS')
675     build.add_argument('--link-opts', action='store', dest='link_opts', \
676                         help='Extra linking options USER_LINK_OPTS')
677     build.add_argument('--no-dbus', action='store', dest='no_dbus', default=False, \
678                         help='Do not run dbus session before packaging')
679
680     return parser.parse_args(argv[1:])
681
682 def build_main(args):
683     """Command [build] entry point."""
684
685     try:
686         my_source = Source(src=args.workspace)
687
688         my_source.set_user_options(c_opts=args.c_opts, cpp_opts=args.cpp_opts, link_opts=args.link_opts)
689         print '-------------------'
690         print '(%s)' % args.profiletosearch
691         print '-------------------'
692         my_sdk = Sdk(sdkpath=args.sdkpath, rootstrap_search=args.profiletosearch, no_dbus=args.no_dbus)
693         my_sdk.clean(my_source)
694         my_sdk.build_tizen(my_source, rootstrap=args.rootstrap, arch=args.arch, jobs=args.jobs)
695         if args.conf == 'Debug' :
696             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert)
697             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
698         else :
699             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
700
701     except Exception as err:
702         wrk = os.path.join(os.path.abspath(args.workspace), '_abs_out_')
703         if not os.path.isdir(wrk):
704             os.makedirs(wrk)
705         with open(os.path.join(wrk, 'build_EXCEPTION.log'), 'w') as ef:
706             ef.write('Exception %s' % str(err))
707         raise err
708
709 def main(argv):
710     """Script entry point."""
711
712     args = argument_parsing(argv)
713
714     if args.subcommands == 'build':
715         return build_main(args)
716     else:
717         print 'Unsupported command %s' % args.subcommands
718         raise LocalError('Command %s not supported' % args.subcommands)
719
720 if __name__ == '__main__':
721
722     try:
723         sys.exit(main(sys.argv))
724     except Exception, e:
725         print 'Exception %s' % str(e)
726         #FIXME: Remove hard-coded output directory.
727         if not os.path.isdir('_abs_out_'):
728             os.makedirs('_abs_out_')
729         with open(os.path.join('_abs_out_', 'build_EXCEPTION.log'), 'w') as ef:
730             ef.write('Exception %s' % repr(e))
731         if 'keystore password was incorrect' in repr(e):
732             sys.exit(99)
733         sys.exit(77)