test 6
[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 package(self, source, cert=None, pkg_type=None, conf='Debug', manual_strip=False):
401         """SDK CLI package command
402             IF Debug + Manual Strip off then generate package-name-debug.tpk
403             IF Debug + Manual Strip on then generate package-name.tpk with custom strip
404             IF Release then generate package-name.tpk with strip option
405         """
406         if cert is None: cert = 'ABS'
407         if pkg_type is None: pkg_type = 'tpk'
408         if conf is None: conf = 'Debug'
409
410         final_app = ''
411         main_args = ['-t', pkg_type, '-s', cert]
412         main_args_web = ['-t', 'wgt', '-s', cert]
413         out = '' #logfile
414
415         # remove tpk or zip file on project path
416         package_list = []
417         for i, x in enumerate(source.project_list):
418             package_list.extend(list_files(os.path.join(x['path'], conf), ext='tpk'))
419             package_list.extend(list_files(os.path.join(x['path'], conf), ext='zip'))
420             package_list.extend(list_files(x['path'], ext='wgt'))
421
422         for k in package_list :
423             print ' package list ' + k;
424             os.remove(k)
425
426         # Manual strip
427         if manual_strip == True :
428             main_args.extend(['--strip', 'on'])
429             strip_cmd='';
430             if self.arch == None:
431                 raise LocalError('Architecture is None')
432
433             for i, x in enumerate(source.project_list):
434                 dir = os.path.join(x['path'], conf)
435                 if not os.path.isdir(dir):
436                     continue
437                 files = [os.path.join(dir,f) for f in os.listdir(dir) if os.path.isfile(os.path.join(dir,f))]
438
439                 # dir must be "project/Debug directory"
440                 info_path = os.path.join(dir, "build.info")
441                 # Default Strip gcc version is 6.2
442                 gcc_version = "6.2"
443                 # Parsing GCC version from build.info inside Debug directory in Project to each project
444                 if os.path.exists(info_path) :
445                     with open(info_path) as fp :
446                         for line in fp :
447                             if line.startswith("toolchain=") :
448                                 line = line.strip()
449                                 gcc_version = re.findall("\d+\.\d+", line)[0]
450                 else :
451                     print "Cannot find Debug/build.info. The default gcc will strip tpk"
452
453                 print "gcc version:" + gcc_version
454
455                 if self.arch == 'x86' :
456                     if(gcc_version == "4.9"):
457                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i386-linux-gnueabi-gcc-' + gcc_version + '/bin/i386-linux-gnueabi-strip')
458                     else:
459                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i586-linux-gnueabi-gcc-' + gcc_version + '/bin/i586-linux-gnueabi-strip')
460                 elif self.arch == 'arm' :
461                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../arm-linux-gnueabi-gcc-' + gcc_version + '/bin/arm-linux-gnueabi-strip')
462                 elif self.arch == 'x86_64' :
463                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../x86_64-linux-gnu-gcc-' + gcc_version + '/bin/x86_64-linux-gnu-strip')
464                 elif self.arch == 'aarch64' :
465                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../aarch64-linux-gnu-gcc-' + gcc_version + '/bin/aarch64-linux-gnu-strip')
466
467                 print strip_cmd
468
469                 for k in files:
470                     cmdline = strip_cmd + ' ' + k;
471                     Executor().run(cmdline, show=False)
472
473         elif conf == 'Release':
474             main_args.extend(['--strip', 'on'])
475
476         for i, x in enumerate(source.project_list):
477             if x['web_app'] == False:
478                 if x['type'] == 'app':
479                     print '\n\n PACKAGE NATIVE\n'
480                     out = '%s\n%s' % (out, \
481                           self._run('package', main_args + ['--',os.path.join(x['path'],conf)]))
482                     try:
483                         final_app = list_files(os.path.join(x['path'], conf), ext='tpk')[0]
484                     except:
485                         raise LocalError('TPK file not generated for %s.' % x['APPNAME'])
486                     x['out_package'] = final_app
487                 elif x['type'] == 'sharedLib':
488                     self._package_sharedlib(x['path'], conf, x['APPNAME'])
489                     x['out_package'] = list_files(os.path.join(x['path'], conf), ext='zip')[0]
490                 else:
491                     raise LocalError('Not supported project type %s' % x['type'])
492             elif x['web_app'] == True:
493                 print '\n\n PACKAGE WEB\n'
494                 out = '%s\n%s' % (out, \
495                       self._run('package', main_args_web + ['--', os.path.join(x['path'], '.buildResult')]))
496                 try:
497                     final_app = list_files(os.path.join(x['path'], '.buildResult'), ext='wgt')[0]
498                 except:
499                     raise LocalError('WGT file not generated for %s.' % x['APPNAME'])
500                 x['out_package'] = final_app
501
502         if source.b_multi == True:
503             extra_args=[]
504             print 'THIS IS MULTI PROJECT'
505             for i, x in enumerate(source.project_list):
506                 if x['out_package'] != final_app and x.get('type') == 'app':
507                     extra_args.extend(['-r', '"%s"' % x['out_package']])
508                 elif x.get('type') == 'sharedLib':
509                     extra_args.extend(['-r', '"%s"' % x['out_package']])
510
511             extra_args.extend(['--', '"%s"' % final_app])
512             if final_app.endswith('.tpk'):
513                 out = self._run('package', main_args + extra_args)
514             elif final_app.endswith('.wgt'):
515                 out = self._run('package', main_args_web + extra_args)
516
517         #TODO: signature validation check failed : Invalid file reference. An unsigned file was found.
518         if final_app.endswith('.tpk'):
519             print 'Packaging final step again!'
520             out = self._run('package', main_args + ['--', '"%s"' % final_app])
521
522         #Append arch to web binary
523         #if final_app.endswith('.wgt'):
524         #    final_app_with_arch = final_app.replace('.wgt', '-%s.wgt' % self.arch)
525         #    os.rename(final_app, final_app_with_arch)
526         #    final_app = final_app_with_arch
527
528         #Copy tpk to output directory
529         if conf == 'Debug' and manual_strip == False :
530             basename = os.path.splitext(final_app)[0]
531             if final_app.endswith('.tpk'):
532                 newname = basename +'-debug.tpk'
533             elif final_app.endswith('.wgt'):
534                 newname = basename +'-debug.wgt'
535             os.rename(final_app, newname)
536             shutil.copy(newname, source.output_dir)
537         else :
538             shutil.copy(final_app, source.output_dir)
539
540     def clean(self, source):
541         """SDK CLI clean command"""
542
543         if os.path.isdir(source.multizip_path):
544             shutil.rmtree(source.multizip_path)
545
546         if os.path.isfile(os.path.join(source.multizip_path, '.zip')):
547             os.remove(os.path.join(source.multizip_path, '.zip'))
548
549         for x in source.project_list:
550             self._run('clean', ['--', x['path']], show=False)
551
552 class Source(object):
553     """Project source related job"""
554
555     workspace = '' #Project root directory
556     project_list = []
557     b_multi = False
558     multi_conf_file = 'WORKSPACE' #Assume multi-project if this file exist.
559     multizip_path = '' #For multi-project packaging -r option
560     property_dict = {}
561     output_dir = '_abs_out_'
562
563     def __init__(self, src=None):
564
565         if src == None:
566             self.workspace = os.getcwd()
567         else:
568             self.workspace = os.path.abspath(src)
569         self.output_dir = os.path.join(self.workspace, self.output_dir)
570
571         os.environ['workspace_loc']=str(os.path.realpath(self.workspace))
572
573         self.multizip_path = os.path.join(self.workspace, 'multizip')
574         self.pre_process()
575
576     def set_properties(self, path):
577         """Fetch all properties from project_def.prop"""
578
579         mydict = {}
580         cp = ConfigParser.SafeConfigParser()
581         cp.optionxform = str
582         if self.is_web_app(path):
583             mydict['web_app'] = True
584         else:
585             mydict['web_app'] = False
586             cp.readfp(FakeSecHead(open(os.path.join(path, 'project_def.prop'))))
587             for x in cp.items('ascection'):
588                 mydict[x[0]] = x[1]
589         mydict['path'] = path
590         return mydict
591
592     def set_user_options(self, c_opts=None, cpp_opts=None, link_opts=None):
593         if c_opts is not None:
594             os.environ['USER_C_OPTS'] = c_opts
595             print 'Set USER_C_OPTS=[%s]' % os.getenv('USER_C_OPTS')
596         if cpp_opts is not None:
597             os.environ['USER_CPP_OPTS'] = cpp_opts
598             print 'Set USER_CPP_OPTS=[%s]' % os.getenv('USER_CPP_OPTS')
599         if link_opts is not None:
600             os.environ['USER_LINK_OPTS'] = link_opts
601             print 'Set USER_LINK_OPTS=[%s]' % os.getenv('USER_LINK_OPTS')
602
603     def is_web_app(self, project_directory):
604         if os.path.isfile(os.path.join(project_directory, 'config.xml')):
605             return True
606         return False
607
608     def pre_process(self):
609
610         if os.path.isfile(os.path.join(self.workspace, self.multi_conf_file)):
611             self.b_multi = True
612             with open(os.path.join(self.workspace, self.multi_conf_file)) as f:
613                 for line in f:
614                     if not line.strip():
615                         continue
616                     file_path = os.path.join(self.workspace, line.rstrip())
617                     self.project_list.append(self.set_properties(file_path))
618         else:
619             self.b_multi = False
620             file_path = os.path.join(self.workspace)
621             self.project_list.append(self.set_properties(file_path))
622
623 def argument_parsing(argv):
624     """Any arguments passed from user"""
625
626     parser = argparse.ArgumentParser(description='ABS command line interface')
627
628     subparsers = parser.add_subparsers(dest='subcommands')
629
630     #### [subcommand - BUILD] ####
631     build = subparsers.add_parser('build')
632     build.add_argument('-w', '--workspace', action='store', dest='workspace', \
633                         help='source directory')
634     build.add_argument('-r', '--rootstrap', action='store', dest='rootstrap', \
635                         help='(ex, mobile-3.0-device.core) rootstrap name')
636     build.add_argument('-a', '--arch', action='store', dest='arch', \
637                         help='(x86|arm|x86_64|aarch64) Architecture to build')
638     build.add_argument('-t', '--type', action='store', dest='type', \
639                         help='(tpk|wgt) Packaging type')
640     build.add_argument('-s', '--cert', action='store', dest='cert', \
641                         help='(ex, ABS) Certificate profile name')
642     build.add_argument('-c', '--conf', action='store',default='Release', dest='conf', \
643                         help='(ex, Debug|Release) Build Configuration')
644     build.add_argument('-j', '--jobs', action='store', dest='jobs', \
645                         help='(number of jobs) The number of parallel builds')
646     build.add_argument('--sdkpath', action='store', dest='sdkpath', \
647                         help='Specify Tizen SDK installation root (one time init).' \
648                              ' ex) /home/yours/tizen-sdk/')
649     build.add_argument('--profile-to-search', action='store', dest='profiletosearch', \
650                         help='Rootstrap profile prefix.' \
651                              ' ex) (mobile|wearable|da-hfp)')
652     build.add_argument('--c-opts', action='store', dest='c_opts', \
653                         help='Extra compile options USER_C_OPTS')
654     build.add_argument('--cpp-opts', action='store', dest='cpp_opts', \
655                         help='Extra compile options USER_CPP_OPTS')
656     build.add_argument('--link-opts', action='store', dest='link_opts', \
657                         help='Extra linking options USER_LINK_OPTS')
658
659     return parser.parse_args(argv[1:])
660
661 def build_main(args):
662     """Command [build] entry point."""
663
664     try:
665         my_source = Source(src=args.workspace)
666
667         my_source.set_user_options(c_opts=args.c_opts, cpp_opts=args.cpp_opts, link_opts=args.link_opts)
668         print '-------------------'
669         print '(%s)' % args.profiletosearch
670         print '-------------------'
671         my_sdk = Sdk(sdkpath=args.sdkpath, rootstrap_search=args.profiletosearch)
672         my_sdk.clean(my_source)
673         my_sdk.build_tizen(my_source, rootstrap=args.rootstrap, arch=args.arch, jobs=args.jobs)
674         if args.conf == 'Debug' :
675             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert)
676             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
677         else :
678             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
679
680     except Exception as err:
681         wrk = os.path.join(os.path.abspath(args.workspace), '_abs_out_')
682         if not os.path.isdir(wrk):
683             os.makedirs(wrk)
684         with open(os.path.join(wrk, 'build_EXCEPTION.log'), 'w') as ef:
685             ef.write('Exception %s' % str(err))
686         raise err
687
688 def main(argv):
689     """Script entry point."""
690
691     args = argument_parsing(argv)
692
693     if args.subcommands == 'build':
694         return build_main(args)
695     else:
696         print 'Unsupported command %s' % args.subcommands
697         raise LocalError('Command %s not supported' % args.subcommands)
698
699 if __name__ == '__main__':
700
701     try:
702         sys.exit(main(sys.argv))
703     except Exception, e:
704         print 'Exception %s' % str(e)
705         #FIXME: Remove hard-coded output directory.
706         if not os.path.isdir('_abs_out_'):
707             os.makedirs('_abs_out_')
708         with open(os.path.join('_abs_out_', 'build_EXCEPTION.log'), 'w') as ef:
709             ef.write('Exception %s' % repr(e))
710         sys.exit(1)