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