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