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